blob: abc4d5d732bcbbb0408c4fcad34f210d9b1b1354 [file] [log] [blame]
Antoine Pitrou37dc5f82011-04-03 17:05:46 +02001/* _bz2 - Low-level Python interface to libbzip2. */
2
3#define PY_SSIZE_T_CLEAN
4
5#include "Python.h"
6#include "structmember.h"
7
8#ifdef WITH_THREAD
9#include "pythread.h"
10#endif
11
12#include <bzlib.h>
13#include <stdio.h>
14
15
16#ifndef BZ_CONFIG_ERROR
17#define BZ2_bzCompress bzCompress
18#define BZ2_bzCompressInit bzCompressInit
19#define BZ2_bzCompressEnd bzCompressEnd
20#define BZ2_bzDecompress bzDecompress
21#define BZ2_bzDecompressInit bzDecompressInit
22#define BZ2_bzDecompressEnd bzDecompressEnd
23#endif /* ! BZ_CONFIG_ERROR */
24
25
26#ifdef WITH_THREAD
27#define ACQUIRE_LOCK(obj) do { \
28 if (!PyThread_acquire_lock((obj)->lock, 0)) { \
29 Py_BEGIN_ALLOW_THREADS \
30 PyThread_acquire_lock((obj)->lock, 1); \
31 Py_END_ALLOW_THREADS \
32 } } while (0)
33#define RELEASE_LOCK(obj) PyThread_release_lock((obj)->lock)
34#else
35#define ACQUIRE_LOCK(obj)
36#define RELEASE_LOCK(obj)
37#endif
38
39
40typedef struct {
41 PyObject_HEAD
42 bz_stream bzs;
43 int flushed;
44#ifdef WITH_THREAD
45 PyThread_type_lock lock;
46#endif
47} BZ2Compressor;
48
49typedef struct {
50 PyObject_HEAD
51 bz_stream bzs;
52 char eof; /* T_BOOL expects a char */
53 PyObject *unused_data;
54#ifdef WITH_THREAD
55 PyThread_type_lock lock;
56#endif
57} BZ2Decompressor;
58
59
60/* Helper functions. */
61
62static int
63catch_bz2_error(int bzerror)
64{
65 switch(bzerror) {
66 case BZ_OK:
67 case BZ_RUN_OK:
68 case BZ_FLUSH_OK:
69 case BZ_FINISH_OK:
70 case BZ_STREAM_END:
71 return 0;
72
73#ifdef BZ_CONFIG_ERROR
74 case BZ_CONFIG_ERROR:
75 PyErr_SetString(PyExc_SystemError,
76 "libbzip2 was not compiled correctly");
77 return 1;
78#endif
79 case BZ_PARAM_ERROR:
80 PyErr_SetString(PyExc_ValueError,
81 "Internal error - "
82 "invalid parameters passed to libbzip2");
83 return 1;
84 case BZ_MEM_ERROR:
85 PyErr_NoMemory();
86 return 1;
87 case BZ_DATA_ERROR:
88 case BZ_DATA_ERROR_MAGIC:
89 PyErr_SetString(PyExc_IOError, "Invalid data stream");
90 return 1;
91 case BZ_IO_ERROR:
92 PyErr_SetString(PyExc_IOError, "Unknown I/O error");
93 return 1;
94 case BZ_UNEXPECTED_EOF:
95 PyErr_SetString(PyExc_EOFError,
96 "Compressed file ended before the logical "
97 "end-of-stream was detected");
98 return 1;
99 case BZ_SEQUENCE_ERROR:
100 PyErr_SetString(PyExc_RuntimeError,
101 "Internal error - "
102 "Invalid sequence of commands sent to libbzip2");
103 return 1;
104 default:
105 PyErr_Format(PyExc_IOError,
106 "Unrecognized error from libbzip2: %d", bzerror);
107 return 1;
108 }
109}
110
111#if BUFSIZ < 8192
112#define SMALLCHUNK 8192
113#else
114#define SMALLCHUNK BUFSIZ
115#endif
116
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200117static int
118grow_buffer(PyObject **buf)
119{
Nadeem Vawda72d6a132011-10-13 13:38:14 +0200120 /* Expand the buffer by an amount proportional to the current size,
121 giving us amortized linear-time behavior. Use a less-than-double
122 growth factor to avoid excessive allocation. */
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200123 size_t size = PyBytes_GET_SIZE(*buf);
Nadeem Vawda18b7fcc2012-10-21 21:16:58 +0200124 size_t new_size = size + (size >> 3) + 6;
125 if (new_size > size) {
126 return _PyBytes_Resize(buf, new_size);
127 } else { /* overflow */
128 PyErr_SetString(PyExc_OverflowError,
129 "Unable to allocate buffer - output too large");
130 return -1;
131 }
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200132}
133
134
135/* BZ2Compressor class. */
136
137static PyObject *
138compress(BZ2Compressor *c, char *data, size_t len, int action)
139{
140 size_t data_size = 0;
141 PyObject *result;
142
143 result = PyBytes_FromStringAndSize(NULL, SMALLCHUNK);
144 if (result == NULL)
145 return NULL;
Nadeem Vawda57cb81d2013-01-02 23:05:56 +0100146
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200147 c->bzs.next_in = data;
Nadeem Vawda57cb81d2013-01-02 23:05:56 +0100148 c->bzs.avail_in = 0;
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200149 c->bzs.next_out = PyBytes_AS_STRING(result);
Victor Stinnerfbf50d42013-06-04 23:18:48 +0200150 c->bzs.avail_out = SMALLCHUNK;
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200151 for (;;) {
152 char *this_out;
153 int bzerror;
154
Nadeem Vawda57cb81d2013-01-02 23:05:56 +0100155 /* On a 64-bit system, len might not fit in avail_in (an unsigned int).
156 Do compression in chunks of no more than UINT_MAX bytes each. */
Nadeem Vawdaea4b46f2011-04-12 23:02:42 +0200157 if (c->bzs.avail_in == 0 && len > 0) {
Victor Stinnerfbf50d42013-06-04 23:18:48 +0200158 c->bzs.avail_in = (unsigned int)Py_MIN(len, UINT_MAX);
Nadeem Vawdaea4b46f2011-04-12 23:02:42 +0200159 len -= c->bzs.avail_in;
160 }
161
Nadeem Vawda57cb81d2013-01-02 23:05:56 +0100162 /* In regular compression mode, stop when input data is exhausted. */
163 if (action == BZ_RUN && c->bzs.avail_in == 0)
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200164 break;
165
166 if (c->bzs.avail_out == 0) {
Nadeem Vawda18b7fcc2012-10-21 21:16:58 +0200167 size_t buffer_left = PyBytes_GET_SIZE(result) - data_size;
168 if (buffer_left == 0) {
169 if (grow_buffer(&result) < 0)
170 goto error;
171 c->bzs.next_out = PyBytes_AS_STRING(result) + data_size;
172 buffer_left = PyBytes_GET_SIZE(result) - data_size;
173 }
Victor Stinnerfbf50d42013-06-04 23:18:48 +0200174 c->bzs.avail_out = (unsigned int)Py_MIN(buffer_left, UINT_MAX);
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200175 }
Nadeem Vawda57cb81d2013-01-02 23:05:56 +0100176
177 Py_BEGIN_ALLOW_THREADS
178 this_out = c->bzs.next_out;
179 bzerror = BZ2_bzCompress(&c->bzs, action);
180 data_size += c->bzs.next_out - this_out;
181 Py_END_ALLOW_THREADS
182 if (catch_bz2_error(bzerror))
183 goto error;
184
185 /* In flushing mode, stop when all buffered data has been flushed. */
186 if (action == BZ_FINISH && bzerror == BZ_STREAM_END)
187 break;
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200188 }
189 if (data_size != PyBytes_GET_SIZE(result))
190 if (_PyBytes_Resize(&result, data_size) < 0)
191 goto error;
192 return result;
193
194error:
195 Py_XDECREF(result);
196 return NULL;
197}
198
199PyDoc_STRVAR(BZ2Compressor_compress__doc__,
200"compress(data) -> bytes\n"
201"\n"
202"Provide data to the compressor object. Returns a chunk of\n"
203"compressed data if possible, or b'' otherwise.\n"
204"\n"
205"When you have finished providing data to the compressor, call the\n"
206"flush() method to finish the compression process.\n");
207
208static PyObject *
209BZ2Compressor_compress(BZ2Compressor *self, PyObject *args)
210{
211 Py_buffer buffer;
212 PyObject *result = NULL;
213
214 if (!PyArg_ParseTuple(args, "y*:compress", &buffer))
215 return NULL;
216
217 ACQUIRE_LOCK(self);
218 if (self->flushed)
219 PyErr_SetString(PyExc_ValueError, "Compressor has been flushed");
220 else
221 result = compress(self, buffer.buf, buffer.len, BZ_RUN);
222 RELEASE_LOCK(self);
223 PyBuffer_Release(&buffer);
224 return result;
225}
226
227PyDoc_STRVAR(BZ2Compressor_flush__doc__,
228"flush() -> bytes\n"
229"\n"
230"Finish the compression process. Returns the compressed data left\n"
231"in internal buffers.\n"
232"\n"
233"The compressor object may not be used after this method is called.\n");
234
235static PyObject *
236BZ2Compressor_flush(BZ2Compressor *self, PyObject *noargs)
237{
238 PyObject *result = NULL;
239
240 ACQUIRE_LOCK(self);
241 if (self->flushed)
242 PyErr_SetString(PyExc_ValueError, "Repeated call to flush()");
243 else {
244 self->flushed = 1;
245 result = compress(self, NULL, 0, BZ_FINISH);
246 }
247 RELEASE_LOCK(self);
248 return result;
249}
250
Victor Stinner5064a522013-07-07 16:50:27 +0200251static void*
252BZ2_Malloc(void* ctx, int items, int size)
253{
254 if (items < 0 || size < 0)
255 return NULL;
256 if ((size_t)items > (size_t)PY_SSIZE_T_MAX / (size_t)size)
257 return NULL;
258 /* PyMem_Malloc() cannot be used: compress() and decompress()
259 release the GIL */
260 return PyMem_RawMalloc(items * size);
261}
262
263static void
264BZ2_Free(void* ctx, void *ptr)
265{
Victor Stinnerb7f1f652013-07-07 17:10:34 +0200266 PyMem_RawFree(ptr);
Victor Stinner5064a522013-07-07 16:50:27 +0200267}
268
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200269static int
270BZ2Compressor_init(BZ2Compressor *self, PyObject *args, PyObject *kwargs)
271{
272 int compresslevel = 9;
273 int bzerror;
274
275 if (!PyArg_ParseTuple(args, "|i:BZ2Compressor", &compresslevel))
276 return -1;
277 if (!(1 <= compresslevel && compresslevel <= 9)) {
278 PyErr_SetString(PyExc_ValueError,
279 "compresslevel must be between 1 and 9");
280 return -1;
281 }
282
283#ifdef WITH_THREAD
284 self->lock = PyThread_allocate_lock();
285 if (self->lock == NULL) {
286 PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock");
287 return -1;
288 }
289#endif
290
Victor Stinner5064a522013-07-07 16:50:27 +0200291 self->bzs.opaque = NULL;
292 self->bzs.bzalloc = BZ2_Malloc;
293 self->bzs.bzfree = BZ2_Free;
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200294 bzerror = BZ2_bzCompressInit(&self->bzs, compresslevel, 0, 0);
295 if (catch_bz2_error(bzerror))
296 goto error;
297
298 return 0;
299
300error:
301#ifdef WITH_THREAD
302 PyThread_free_lock(self->lock);
303 self->lock = NULL;
304#endif
305 return -1;
306}
307
308static void
309BZ2Compressor_dealloc(BZ2Compressor *self)
310{
311 BZ2_bzCompressEnd(&self->bzs);
312#ifdef WITH_THREAD
313 if (self->lock != NULL)
314 PyThread_free_lock(self->lock);
315#endif
316 Py_TYPE(self)->tp_free((PyObject *)self);
317}
318
319static PyMethodDef BZ2Compressor_methods[] = {
320 {"compress", (PyCFunction)BZ2Compressor_compress, METH_VARARGS,
321 BZ2Compressor_compress__doc__},
322 {"flush", (PyCFunction)BZ2Compressor_flush, METH_NOARGS,
323 BZ2Compressor_flush__doc__},
324 {NULL}
325};
326
327PyDoc_STRVAR(BZ2Compressor__doc__,
328"BZ2Compressor(compresslevel=9)\n"
329"\n"
330"Create a compressor object for compressing data incrementally.\n"
331"\n"
332"compresslevel, if given, must be a number between 1 and 9.\n"
333"\n"
334"For one-shot compression, use the compress() function instead.\n");
335
336static PyTypeObject BZ2Compressor_Type = {
337 PyVarObject_HEAD_INIT(NULL, 0)
338 "_bz2.BZ2Compressor", /* tp_name */
339 sizeof(BZ2Compressor), /* tp_basicsize */
340 0, /* tp_itemsize */
341 (destructor)BZ2Compressor_dealloc, /* tp_dealloc */
342 0, /* tp_print */
343 0, /* tp_getattr */
344 0, /* tp_setattr */
345 0, /* tp_reserved */
346 0, /* tp_repr */
347 0, /* tp_as_number */
348 0, /* tp_as_sequence */
349 0, /* tp_as_mapping */
350 0, /* tp_hash */
351 0, /* tp_call */
352 0, /* tp_str */
353 0, /* tp_getattro */
354 0, /* tp_setattro */
355 0, /* tp_as_buffer */
356 Py_TPFLAGS_DEFAULT, /* tp_flags */
357 BZ2Compressor__doc__, /* tp_doc */
358 0, /* tp_traverse */
359 0, /* tp_clear */
360 0, /* tp_richcompare */
361 0, /* tp_weaklistoffset */
362 0, /* tp_iter */
363 0, /* tp_iternext */
364 BZ2Compressor_methods, /* tp_methods */
365 0, /* tp_members */
366 0, /* tp_getset */
367 0, /* tp_base */
368 0, /* tp_dict */
369 0, /* tp_descr_get */
370 0, /* tp_descr_set */
371 0, /* tp_dictoffset */
372 (initproc)BZ2Compressor_init, /* tp_init */
373 0, /* tp_alloc */
374 PyType_GenericNew, /* tp_new */
375};
376
377
378/* BZ2Decompressor class. */
379
380static PyObject *
381decompress(BZ2Decompressor *d, char *data, size_t len)
382{
383 size_t data_size = 0;
384 PyObject *result;
385
386 result = PyBytes_FromStringAndSize(NULL, SMALLCHUNK);
387 if (result == NULL)
388 return result;
389 d->bzs.next_in = data;
Nadeem Vawdaea4b46f2011-04-12 23:02:42 +0200390 /* On a 64-bit system, len might not fit in avail_in (an unsigned int).
391 Do decompression in chunks of no more than UINT_MAX bytes each. */
Victor Stinnerfbf50d42013-06-04 23:18:48 +0200392 d->bzs.avail_in = (unsigned int)Py_MIN(len, UINT_MAX);
Nadeem Vawdaea4b46f2011-04-12 23:02:42 +0200393 len -= d->bzs.avail_in;
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200394 d->bzs.next_out = PyBytes_AS_STRING(result);
Victor Stinnerfbf50d42013-06-04 23:18:48 +0200395 d->bzs.avail_out = SMALLCHUNK;
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200396 for (;;) {
397 char *this_out;
398 int bzerror;
399
400 Py_BEGIN_ALLOW_THREADS
401 this_out = d->bzs.next_out;
402 bzerror = BZ2_bzDecompress(&d->bzs);
403 data_size += d->bzs.next_out - this_out;
404 Py_END_ALLOW_THREADS
405 if (catch_bz2_error(bzerror))
406 goto error;
407 if (bzerror == BZ_STREAM_END) {
408 d->eof = 1;
Nadeem Vawdaea4b46f2011-04-12 23:02:42 +0200409 len += d->bzs.avail_in;
410 if (len > 0) { /* Save leftover input to unused_data */
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200411 Py_CLEAR(d->unused_data);
Nadeem Vawdaea4b46f2011-04-12 23:02:42 +0200412 d->unused_data = PyBytes_FromStringAndSize(d->bzs.next_in, len);
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200413 if (d->unused_data == NULL)
414 goto error;
415 }
416 break;
417 }
Nadeem Vawdaea4b46f2011-04-12 23:02:42 +0200418 if (d->bzs.avail_in == 0) {
419 if (len == 0)
420 break;
Victor Stinnerfbf50d42013-06-04 23:18:48 +0200421 d->bzs.avail_in = (unsigned int)Py_MIN(len, UINT_MAX);
Nadeem Vawdaea4b46f2011-04-12 23:02:42 +0200422 len -= d->bzs.avail_in;
423 }
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200424 if (d->bzs.avail_out == 0) {
Nadeem Vawda18b7fcc2012-10-21 21:16:58 +0200425 size_t buffer_left = PyBytes_GET_SIZE(result) - data_size;
426 if (buffer_left == 0) {
427 if (grow_buffer(&result) < 0)
428 goto error;
429 d->bzs.next_out = PyBytes_AS_STRING(result) + data_size;
430 buffer_left = PyBytes_GET_SIZE(result) - data_size;
431 }
Victor Stinnerfbf50d42013-06-04 23:18:48 +0200432 d->bzs.avail_out = (unsigned int)Py_MIN(buffer_left, UINT_MAX);
Antoine Pitrou37dc5f82011-04-03 17:05:46 +0200433 }
434 }
435 if (data_size != PyBytes_GET_SIZE(result))
436 if (_PyBytes_Resize(&result, data_size) < 0)
437 goto error;
438 return result;
439
440error:
441 Py_XDECREF(result);
442 return NULL;
443}
444
445PyDoc_STRVAR(BZ2Decompressor_decompress__doc__,
446"decompress(data) -> bytes\n"
447"\n"
448"Provide data to the decompressor object. Returns a chunk of\n"
449"decompressed data if possible, or b'' otherwise.\n"
450"\n"
451"Attempting to decompress data after the end of stream is reached\n"
452"raises an EOFError. Any data found after the end of the stream\n"
453"is ignored and saved in the unused_data attribute.\n");
454
455static PyObject *
456BZ2Decompressor_decompress(BZ2Decompressor *self, PyObject *args)
457{
458 Py_buffer buffer;
459 PyObject *result = NULL;
460
461 if (!PyArg_ParseTuple(args, "y*:decompress", &buffer))
462 return NULL;
463
464 ACQUIRE_LOCK(self);
465 if (self->eof)
466 PyErr_SetString(PyExc_EOFError, "End of stream already reached");
467 else
468 result = decompress(self, buffer.buf, buffer.len);
469 RELEASE_LOCK(self);
470 PyBuffer_Release(&buffer);
471 return result;
472}
473
474static int
475BZ2Decompressor_init(BZ2Decompressor *self, PyObject *args, PyObject *kwargs)
476{
477 int bzerror;
478
479 if (!PyArg_ParseTuple(args, ":BZ2Decompressor"))
480 return -1;
481
482#ifdef WITH_THREAD
483 self->lock = PyThread_allocate_lock();
484 if (self->lock == NULL) {
485 PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock");
486 return -1;
487 }
488#endif
489
490 self->unused_data = PyBytes_FromStringAndSize("", 0);
491 if (self->unused_data == NULL)
492 goto error;
493
494 bzerror = BZ2_bzDecompressInit(&self->bzs, 0, 0);
495 if (catch_bz2_error(bzerror))
496 goto error;
497
498 return 0;
499
500error:
501 Py_CLEAR(self->unused_data);
502#ifdef WITH_THREAD
503 PyThread_free_lock(self->lock);
504 self->lock = NULL;
505#endif
506 return -1;
507}
508
509static void
510BZ2Decompressor_dealloc(BZ2Decompressor *self)
511{
512 BZ2_bzDecompressEnd(&self->bzs);
513 Py_CLEAR(self->unused_data);
514#ifdef WITH_THREAD
515 if (self->lock != NULL)
516 PyThread_free_lock(self->lock);
517#endif
518 Py_TYPE(self)->tp_free((PyObject *)self);
519}
520
521static PyMethodDef BZ2Decompressor_methods[] = {
522 {"decompress", (PyCFunction)BZ2Decompressor_decompress, METH_VARARGS,
523 BZ2Decompressor_decompress__doc__},
524 {NULL}
525};
526
527PyDoc_STRVAR(BZ2Decompressor_eof__doc__,
528"True if the end-of-stream marker has been reached.");
529
530PyDoc_STRVAR(BZ2Decompressor_unused_data__doc__,
531"Data found after the end of the compressed stream.");
532
533static PyMemberDef BZ2Decompressor_members[] = {
534 {"eof", T_BOOL, offsetof(BZ2Decompressor, eof),
535 READONLY, BZ2Decompressor_eof__doc__},
536 {"unused_data", T_OBJECT_EX, offsetof(BZ2Decompressor, unused_data),
537 READONLY, BZ2Decompressor_unused_data__doc__},
538 {NULL}
539};
540
541PyDoc_STRVAR(BZ2Decompressor__doc__,
542"BZ2Decompressor()\n"
543"\n"
544"Create a decompressor object for decompressing data incrementally.\n"
545"\n"
546"For one-shot decompression, use the decompress() function instead.\n");
547
548static PyTypeObject BZ2Decompressor_Type = {
549 PyVarObject_HEAD_INIT(NULL, 0)
550 "_bz2.BZ2Decompressor", /* tp_name */
551 sizeof(BZ2Decompressor), /* tp_basicsize */
552 0, /* tp_itemsize */
553 (destructor)BZ2Decompressor_dealloc,/* tp_dealloc */
554 0, /* tp_print */
555 0, /* tp_getattr */
556 0, /* tp_setattr */
557 0, /* tp_reserved */
558 0, /* tp_repr */
559 0, /* tp_as_number */
560 0, /* tp_as_sequence */
561 0, /* tp_as_mapping */
562 0, /* tp_hash */
563 0, /* tp_call */
564 0, /* tp_str */
565 0, /* tp_getattro */
566 0, /* tp_setattro */
567 0, /* tp_as_buffer */
568 Py_TPFLAGS_DEFAULT, /* tp_flags */
569 BZ2Decompressor__doc__, /* tp_doc */
570 0, /* tp_traverse */
571 0, /* tp_clear */
572 0, /* tp_richcompare */
573 0, /* tp_weaklistoffset */
574 0, /* tp_iter */
575 0, /* tp_iternext */
576 BZ2Decompressor_methods, /* tp_methods */
577 BZ2Decompressor_members, /* tp_members */
578 0, /* tp_getset */
579 0, /* tp_base */
580 0, /* tp_dict */
581 0, /* tp_descr_get */
582 0, /* tp_descr_set */
583 0, /* tp_dictoffset */
584 (initproc)BZ2Decompressor_init, /* tp_init */
585 0, /* tp_alloc */
586 PyType_GenericNew, /* tp_new */
587};
588
589
590/* Module initialization. */
591
592static struct PyModuleDef _bz2module = {
593 PyModuleDef_HEAD_INIT,
594 "_bz2",
595 NULL,
596 -1,
597 NULL,
598 NULL,
599 NULL,
600 NULL,
601 NULL
602};
603
604PyMODINIT_FUNC
605PyInit__bz2(void)
606{
607 PyObject *m;
608
609 if (PyType_Ready(&BZ2Compressor_Type) < 0)
610 return NULL;
611 if (PyType_Ready(&BZ2Decompressor_Type) < 0)
612 return NULL;
613
614 m = PyModule_Create(&_bz2module);
615 if (m == NULL)
616 return NULL;
617
618 Py_INCREF(&BZ2Compressor_Type);
619 PyModule_AddObject(m, "BZ2Compressor", (PyObject *)&BZ2Compressor_Type);
620
621 Py_INCREF(&BZ2Decompressor_Type);
622 PyModule_AddObject(m, "BZ2Decompressor",
623 (PyObject *)&BZ2Decompressor_Type);
624
625 return m;
626}