126 static PyObject* reader_tell(ZstdCompressionReader* self) { |
126 static PyObject* reader_tell(ZstdCompressionReader* self) { |
127 /* TODO should this raise OSError since stream isn't seekable? */ |
127 /* TODO should this raise OSError since stream isn't seekable? */ |
128 return PyLong_FromUnsignedLongLong(self->bytesCompressed); |
128 return PyLong_FromUnsignedLongLong(self->bytesCompressed); |
129 } |
129 } |
130 |
130 |
|
131 int read_compressor_input(ZstdCompressionReader* self) { |
|
132 if (self->finishedInput) { |
|
133 return 0; |
|
134 } |
|
135 |
|
136 if (self->input.pos != self->input.size) { |
|
137 return 0; |
|
138 } |
|
139 |
|
140 if (self->reader) { |
|
141 Py_buffer buffer; |
|
142 |
|
143 assert(self->readResult == NULL); |
|
144 |
|
145 self->readResult = PyObject_CallMethod(self->reader, "read", |
|
146 "k", self->readSize); |
|
147 |
|
148 if (NULL == self->readResult) { |
|
149 return -1; |
|
150 } |
|
151 |
|
152 memset(&buffer, 0, sizeof(buffer)); |
|
153 |
|
154 if (0 != PyObject_GetBuffer(self->readResult, &buffer, PyBUF_CONTIG_RO)) { |
|
155 return -1; |
|
156 } |
|
157 |
|
158 /* EOF */ |
|
159 if (0 == buffer.len) { |
|
160 self->finishedInput = 1; |
|
161 Py_CLEAR(self->readResult); |
|
162 } |
|
163 else { |
|
164 self->input.src = buffer.buf; |
|
165 self->input.size = buffer.len; |
|
166 self->input.pos = 0; |
|
167 } |
|
168 |
|
169 PyBuffer_Release(&buffer); |
|
170 } |
|
171 else { |
|
172 assert(self->buffer.buf); |
|
173 |
|
174 self->input.src = self->buffer.buf; |
|
175 self->input.size = self->buffer.len; |
|
176 self->input.pos = 0; |
|
177 } |
|
178 |
|
179 return 1; |
|
180 } |
|
181 |
|
182 int compress_input(ZstdCompressionReader* self, ZSTD_outBuffer* output) { |
|
183 size_t oldPos; |
|
184 size_t zresult; |
|
185 |
|
186 /* If we have data left over, consume it. */ |
|
187 if (self->input.pos < self->input.size) { |
|
188 oldPos = output->pos; |
|
189 |
|
190 Py_BEGIN_ALLOW_THREADS |
|
191 zresult = ZSTD_compressStream2(self->compressor->cctx, |
|
192 output, &self->input, ZSTD_e_continue); |
|
193 Py_END_ALLOW_THREADS |
|
194 |
|
195 self->bytesCompressed += output->pos - oldPos; |
|
196 |
|
197 /* Input exhausted. Clear out state tracking. */ |
|
198 if (self->input.pos == self->input.size) { |
|
199 memset(&self->input, 0, sizeof(self->input)); |
|
200 Py_CLEAR(self->readResult); |
|
201 |
|
202 if (self->buffer.buf) { |
|
203 self->finishedInput = 1; |
|
204 } |
|
205 } |
|
206 |
|
207 if (ZSTD_isError(zresult)) { |
|
208 PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult)); |
|
209 return -1; |
|
210 } |
|
211 } |
|
212 |
|
213 if (output->pos && output->pos == output->size) { |
|
214 return 1; |
|
215 } |
|
216 else { |
|
217 return 0; |
|
218 } |
|
219 } |
|
220 |
131 static PyObject* reader_read(ZstdCompressionReader* self, PyObject* args, PyObject* kwargs) { |
221 static PyObject* reader_read(ZstdCompressionReader* self, PyObject* args, PyObject* kwargs) { |
132 static char* kwlist[] = { |
222 static char* kwlist[] = { |
133 "size", |
223 "size", |
134 NULL |
224 NULL |
135 }; |
225 }; |
170 self->output.size = resultSize; |
265 self->output.size = resultSize; |
171 self->output.pos = 0; |
266 self->output.pos = 0; |
172 |
267 |
173 readinput: |
268 readinput: |
174 |
269 |
175 /* If we have data left over, consume it. */ |
270 compressResult = compress_input(self, &self->output); |
176 if (self->input.pos < self->input.size) { |
271 |
177 oldPos = self->output.pos; |
272 if (-1 == compressResult) { |
178 |
273 Py_XDECREF(result); |
179 Py_BEGIN_ALLOW_THREADS |
274 return NULL; |
180 zresult = ZSTD_compress_generic(self->compressor->cctx, |
275 } |
181 &self->output, &self->input, ZSTD_e_continue); |
276 else if (0 == compressResult) { |
182 |
277 /* There is room in the output. We fall through to below, which will |
183 Py_END_ALLOW_THREADS |
278 * either get more input for us or will attempt to end the stream. |
184 |
279 */ |
185 self->bytesCompressed += self->output.pos - oldPos; |
280 } |
186 |
281 else if (1 == compressResult) { |
187 /* Input exhausted. Clear out state tracking. */ |
282 memset(&self->output, 0, sizeof(self->output)); |
188 if (self->input.pos == self->input.size) { |
283 return result; |
189 memset(&self->input, 0, sizeof(self->input)); |
284 } |
190 Py_CLEAR(self->readResult); |
285 else { |
191 |
286 assert(0); |
192 if (self->buffer.buf) { |
287 } |
193 self->finishedInput = 1; |
288 |
194 } |
289 readResult = read_compressor_input(self); |
195 } |
290 |
196 |
291 if (-1 == readResult) { |
197 if (ZSTD_isError(zresult)) { |
292 return NULL; |
198 PyErr_Format(ZstdError, "zstd compress error: %s", ZSTD_getErrorName(zresult)); |
293 } |
199 return NULL; |
294 else if (0 == readResult) { } |
200 } |
295 else if (1 == readResult) { } |
201 |
296 else { |
202 if (self->output.pos) { |
297 assert(0); |
203 /* If no more room in output, emit it. */ |
|
204 if (self->output.pos == self->output.size) { |
|
205 memset(&self->output, 0, sizeof(self->output)); |
|
206 return result; |
|
207 } |
|
208 |
|
209 /* |
|
210 * There is room in the output. We fall through to below, which will either |
|
211 * get more input for us or will attempt to end the stream. |
|
212 */ |
|
213 } |
|
214 |
|
215 /* Fall through to gather more input. */ |
|
216 } |
|
217 |
|
218 if (!self->finishedInput) { |
|
219 if (self->reader) { |
|
220 Py_buffer buffer; |
|
221 |
|
222 assert(self->readResult == NULL); |
|
223 self->readResult = PyObject_CallMethod(self->reader, "read", |
|
224 "k", self->readSize); |
|
225 if (self->readResult == NULL) { |
|
226 return NULL; |
|
227 } |
|
228 |
|
229 memset(&buffer, 0, sizeof(buffer)); |
|
230 |
|
231 if (0 != PyObject_GetBuffer(self->readResult, &buffer, PyBUF_CONTIG_RO)) { |
|
232 return NULL; |
|
233 } |
|
234 |
|
235 /* EOF */ |
|
236 if (0 == buffer.len) { |
|
237 self->finishedInput = 1; |
|
238 Py_CLEAR(self->readResult); |
|
239 } |
|
240 else { |
|
241 self->input.src = buffer.buf; |
|
242 self->input.size = buffer.len; |
|
243 self->input.pos = 0; |
|
244 } |
|
245 |
|
246 PyBuffer_Release(&buffer); |
|
247 } |
|
248 else { |
|
249 assert(self->buffer.buf); |
|
250 |
|
251 self->input.src = self->buffer.buf; |
|
252 self->input.size = self->buffer.len; |
|
253 self->input.pos = 0; |
|
254 } |
|
255 } |
298 } |
256 |
299 |
257 if (self->input.size) { |
300 if (self->input.size) { |
258 goto readinput; |
301 goto readinput; |
259 } |
302 } |
260 |
303 |
261 /* Else EOF */ |
304 /* Else EOF */ |
262 oldPos = self->output.pos; |
305 oldPos = self->output.pos; |
263 |
306 |
264 zresult = ZSTD_compress_generic(self->compressor->cctx, &self->output, |
307 zresult = ZSTD_compressStream2(self->compressor->cctx, &self->output, |
265 &self->input, ZSTD_e_end); |
308 &self->input, ZSTD_e_end); |
266 |
309 |
267 self->bytesCompressed += self->output.pos - oldPos; |
310 self->bytesCompressed += self->output.pos - oldPos; |
268 |
311 |
269 if (ZSTD_isError(zresult)) { |
312 if (ZSTD_isError(zresult)) { |
270 PyErr_Format(ZstdError, "error ending compression stream: %s", |
313 PyErr_Format(ZstdError, "error ending compression stream: %s", |
271 ZSTD_getErrorName(zresult)); |
314 ZSTD_getErrorName(zresult)); |
|
315 Py_XDECREF(result); |
272 return NULL; |
316 return NULL; |
273 } |
317 } |
274 |
318 |
275 assert(self->output.pos); |
319 assert(self->output.pos); |
276 |
320 |
286 memset(&self->output, 0, sizeof(self->output)); |
330 memset(&self->output, 0, sizeof(self->output)); |
287 |
331 |
288 return result; |
332 return result; |
289 } |
333 } |
290 |
334 |
|
335 static PyObject* reader_read1(ZstdCompressionReader* self, PyObject* args, PyObject* kwargs) { |
|
336 static char* kwlist[] = { |
|
337 "size", |
|
338 NULL |
|
339 }; |
|
340 |
|
341 Py_ssize_t size = -1; |
|
342 PyObject* result = NULL; |
|
343 char* resultBuffer; |
|
344 Py_ssize_t resultSize; |
|
345 ZSTD_outBuffer output; |
|
346 int compressResult; |
|
347 size_t oldPos; |
|
348 size_t zresult; |
|
349 |
|
350 if (self->closed) { |
|
351 PyErr_SetString(PyExc_ValueError, "stream is closed"); |
|
352 return NULL; |
|
353 } |
|
354 |
|
355 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|n:read1", kwlist, &size)) { |
|
356 return NULL; |
|
357 } |
|
358 |
|
359 if (size < -1) { |
|
360 PyErr_SetString(PyExc_ValueError, "cannot read negative amounts less than -1"); |
|
361 return NULL; |
|
362 } |
|
363 |
|
364 if (self->finishedOutput || size == 0) { |
|
365 return PyBytes_FromStringAndSize("", 0); |
|
366 } |
|
367 |
|
368 if (size == -1) { |
|
369 size = ZSTD_CStreamOutSize(); |
|
370 } |
|
371 |
|
372 result = PyBytes_FromStringAndSize(NULL, size); |
|
373 if (NULL == result) { |
|
374 return NULL; |
|
375 } |
|
376 |
|
377 PyBytes_AsStringAndSize(result, &resultBuffer, &resultSize); |
|
378 |
|
379 output.dst = resultBuffer; |
|
380 output.size = resultSize; |
|
381 output.pos = 0; |
|
382 |
|
383 /* read1() is supposed to use at most 1 read() from the underlying stream. |
|
384 However, we can't satisfy this requirement with compression because |
|
385 not every input will generate output. We /could/ flush the compressor, |
|
386 but this may not be desirable. We allow multiple read() from the |
|
387 underlying stream. But unlike read(), we return as soon as output data |
|
388 is available. |
|
389 */ |
|
390 |
|
391 compressResult = compress_input(self, &output); |
|
392 |
|
393 if (-1 == compressResult) { |
|
394 Py_XDECREF(result); |
|
395 return NULL; |
|
396 } |
|
397 else if (0 == compressResult || 1 == compressResult) { } |
|
398 else { |
|
399 assert(0); |
|
400 } |
|
401 |
|
402 if (output.pos) { |
|
403 goto finally; |
|
404 } |
|
405 |
|
406 while (!self->finishedInput) { |
|
407 int readResult = read_compressor_input(self); |
|
408 |
|
409 if (-1 == readResult) { |
|
410 Py_XDECREF(result); |
|
411 return NULL; |
|
412 } |
|
413 else if (0 == readResult || 1 == readResult) { } |
|
414 else { |
|
415 assert(0); |
|
416 } |
|
417 |
|
418 compressResult = compress_input(self, &output); |
|
419 |
|
420 if (-1 == compressResult) { |
|
421 Py_XDECREF(result); |
|
422 return NULL; |
|
423 } |
|
424 else if (0 == compressResult || 1 == compressResult) { } |
|
425 else { |
|
426 assert(0); |
|
427 } |
|
428 |
|
429 if (output.pos) { |
|
430 goto finally; |
|
431 } |
|
432 } |
|
433 |
|
434 /* EOF */ |
|
435 oldPos = output.pos; |
|
436 |
|
437 zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input, |
|
438 ZSTD_e_end); |
|
439 |
|
440 self->bytesCompressed += output.pos - oldPos; |
|
441 |
|
442 if (ZSTD_isError(zresult)) { |
|
443 PyErr_Format(ZstdError, "error ending compression stream: %s", |
|
444 ZSTD_getErrorName(zresult)); |
|
445 Py_XDECREF(result); |
|
446 return NULL; |
|
447 } |
|
448 |
|
449 if (zresult == 0) { |
|
450 self->finishedOutput = 1; |
|
451 } |
|
452 |
|
453 finally: |
|
454 if (result) { |
|
455 if (safe_pybytes_resize(&result, output.pos)) { |
|
456 Py_XDECREF(result); |
|
457 return NULL; |
|
458 } |
|
459 } |
|
460 |
|
461 return result; |
|
462 } |
|
463 |
291 static PyObject* reader_readall(PyObject* self) { |
464 static PyObject* reader_readall(PyObject* self) { |
292 PyErr_SetNone(PyExc_NotImplementedError); |
465 PyObject* chunks = NULL; |
293 return NULL; |
466 PyObject* empty = NULL; |
|
467 PyObject* result = NULL; |
|
468 |
|
469 /* Our strategy is to collect chunks into a list then join all the |
|
470 * chunks at the end. We could potentially use e.g. an io.BytesIO. But |
|
471 * this feels simple enough to implement and avoids potentially expensive |
|
472 * reallocations of large buffers. |
|
473 */ |
|
474 chunks = PyList_New(0); |
|
475 if (NULL == chunks) { |
|
476 return NULL; |
|
477 } |
|
478 |
|
479 while (1) { |
|
480 PyObject* chunk = PyObject_CallMethod(self, "read", "i", 1048576); |
|
481 if (NULL == chunk) { |
|
482 Py_DECREF(chunks); |
|
483 return NULL; |
|
484 } |
|
485 |
|
486 if (!PyBytes_Size(chunk)) { |
|
487 Py_DECREF(chunk); |
|
488 break; |
|
489 } |
|
490 |
|
491 if (PyList_Append(chunks, chunk)) { |
|
492 Py_DECREF(chunk); |
|
493 Py_DECREF(chunks); |
|
494 return NULL; |
|
495 } |
|
496 |
|
497 Py_DECREF(chunk); |
|
498 } |
|
499 |
|
500 empty = PyBytes_FromStringAndSize("", 0); |
|
501 if (NULL == empty) { |
|
502 Py_DECREF(chunks); |
|
503 return NULL; |
|
504 } |
|
505 |
|
506 result = PyObject_CallMethod(empty, "join", "O", chunks); |
|
507 |
|
508 Py_DECREF(empty); |
|
509 Py_DECREF(chunks); |
|
510 |
|
511 return result; |
|
512 } |
|
513 |
|
514 static PyObject* reader_readinto(ZstdCompressionReader* self, PyObject* args) { |
|
515 Py_buffer dest; |
|
516 ZSTD_outBuffer output; |
|
517 int readResult, compressResult; |
|
518 PyObject* result = NULL; |
|
519 size_t zresult; |
|
520 size_t oldPos; |
|
521 |
|
522 if (self->closed) { |
|
523 PyErr_SetString(PyExc_ValueError, "stream is closed"); |
|
524 return NULL; |
|
525 } |
|
526 |
|
527 if (self->finishedOutput) { |
|
528 return PyLong_FromLong(0); |
|
529 } |
|
530 |
|
531 if (!PyArg_ParseTuple(args, "w*:readinto", &dest)) { |
|
532 return NULL; |
|
533 } |
|
534 |
|
535 if (!PyBuffer_IsContiguous(&dest, 'C') || dest.ndim > 1) { |
|
536 PyErr_SetString(PyExc_ValueError, |
|
537 "destination buffer should be contiguous and have at most one dimension"); |
|
538 goto finally; |
|
539 } |
|
540 |
|
541 output.dst = dest.buf; |
|
542 output.size = dest.len; |
|
543 output.pos = 0; |
|
544 |
|
545 compressResult = compress_input(self, &output); |
|
546 |
|
547 if (-1 == compressResult) { |
|
548 goto finally; |
|
549 } |
|
550 else if (0 == compressResult) { } |
|
551 else if (1 == compressResult) { |
|
552 result = PyLong_FromSize_t(output.pos); |
|
553 goto finally; |
|
554 } |
|
555 else { |
|
556 assert(0); |
|
557 } |
|
558 |
|
559 while (!self->finishedInput) { |
|
560 readResult = read_compressor_input(self); |
|
561 |
|
562 if (-1 == readResult) { |
|
563 goto finally; |
|
564 } |
|
565 else if (0 == readResult || 1 == readResult) {} |
|
566 else { |
|
567 assert(0); |
|
568 } |
|
569 |
|
570 compressResult = compress_input(self, &output); |
|
571 |
|
572 if (-1 == compressResult) { |
|
573 goto finally; |
|
574 } |
|
575 else if (0 == compressResult) { } |
|
576 else if (1 == compressResult) { |
|
577 result = PyLong_FromSize_t(output.pos); |
|
578 goto finally; |
|
579 } |
|
580 else { |
|
581 assert(0); |
|
582 } |
|
583 } |
|
584 |
|
585 /* EOF */ |
|
586 oldPos = output.pos; |
|
587 |
|
588 zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input, |
|
589 ZSTD_e_end); |
|
590 |
|
591 self->bytesCompressed += self->output.pos - oldPos; |
|
592 |
|
593 if (ZSTD_isError(zresult)) { |
|
594 PyErr_Format(ZstdError, "error ending compression stream: %s", |
|
595 ZSTD_getErrorName(zresult)); |
|
596 goto finally; |
|
597 } |
|
598 |
|
599 assert(output.pos); |
|
600 |
|
601 if (0 == zresult) { |
|
602 self->finishedOutput = 1; |
|
603 } |
|
604 |
|
605 result = PyLong_FromSize_t(output.pos); |
|
606 |
|
607 finally: |
|
608 PyBuffer_Release(&dest); |
|
609 |
|
610 return result; |
|
611 } |
|
612 |
|
613 static PyObject* reader_readinto1(ZstdCompressionReader* self, PyObject* args) { |
|
614 Py_buffer dest; |
|
615 PyObject* result = NULL; |
|
616 ZSTD_outBuffer output; |
|
617 int compressResult; |
|
618 size_t oldPos; |
|
619 size_t zresult; |
|
620 |
|
621 if (self->closed) { |
|
622 PyErr_SetString(PyExc_ValueError, "stream is closed"); |
|
623 return NULL; |
|
624 } |
|
625 |
|
626 if (self->finishedOutput) { |
|
627 return PyLong_FromLong(0); |
|
628 } |
|
629 |
|
630 if (!PyArg_ParseTuple(args, "w*:readinto1", &dest)) { |
|
631 return NULL; |
|
632 } |
|
633 |
|
634 if (!PyBuffer_IsContiguous(&dest, 'C') || dest.ndim > 1) { |
|
635 PyErr_SetString(PyExc_ValueError, |
|
636 "destination buffer should be contiguous and have at most one dimension"); |
|
637 goto finally; |
|
638 } |
|
639 |
|
640 output.dst = dest.buf; |
|
641 output.size = dest.len; |
|
642 output.pos = 0; |
|
643 |
|
644 compressResult = compress_input(self, &output); |
|
645 |
|
646 if (-1 == compressResult) { |
|
647 goto finally; |
|
648 } |
|
649 else if (0 == compressResult || 1 == compressResult) { } |
|
650 else { |
|
651 assert(0); |
|
652 } |
|
653 |
|
654 if (output.pos) { |
|
655 result = PyLong_FromSize_t(output.pos); |
|
656 goto finally; |
|
657 } |
|
658 |
|
659 while (!self->finishedInput) { |
|
660 int readResult = read_compressor_input(self); |
|
661 |
|
662 if (-1 == readResult) { |
|
663 goto finally; |
|
664 } |
|
665 else if (0 == readResult || 1 == readResult) { } |
|
666 else { |
|
667 assert(0); |
|
668 } |
|
669 |
|
670 compressResult = compress_input(self, &output); |
|
671 |
|
672 if (-1 == compressResult) { |
|
673 goto finally; |
|
674 } |
|
675 else if (0 == compressResult) { } |
|
676 else if (1 == compressResult) { |
|
677 result = PyLong_FromSize_t(output.pos); |
|
678 goto finally; |
|
679 } |
|
680 else { |
|
681 assert(0); |
|
682 } |
|
683 |
|
684 /* If we produced output and we're not done with input, emit |
|
685 * that output now, as we've hit restrictions of read1(). |
|
686 */ |
|
687 if (output.pos && !self->finishedInput) { |
|
688 result = PyLong_FromSize_t(output.pos); |
|
689 goto finally; |
|
690 } |
|
691 |
|
692 /* Otherwise we either have no output or we've exhausted the |
|
693 * input. Either we try to get more input or we fall through |
|
694 * to EOF below */ |
|
695 } |
|
696 |
|
697 /* EOF */ |
|
698 oldPos = output.pos; |
|
699 |
|
700 zresult = ZSTD_compressStream2(self->compressor->cctx, &output, &self->input, |
|
701 ZSTD_e_end); |
|
702 |
|
703 self->bytesCompressed += self->output.pos - oldPos; |
|
704 |
|
705 if (ZSTD_isError(zresult)) { |
|
706 PyErr_Format(ZstdError, "error ending compression stream: %s", |
|
707 ZSTD_getErrorName(zresult)); |
|
708 goto finally; |
|
709 } |
|
710 |
|
711 assert(output.pos); |
|
712 |
|
713 if (0 == zresult) { |
|
714 self->finishedOutput = 1; |
|
715 } |
|
716 |
|
717 result = PyLong_FromSize_t(output.pos); |
|
718 |
|
719 finally: |
|
720 PyBuffer_Release(&dest); |
|
721 |
|
722 return result; |
294 } |
723 } |
295 |
724 |
296 static PyObject* reader_iter(PyObject* self) { |
725 static PyObject* reader_iter(PyObject* self) { |
297 set_unsupported_operation(); |
726 set_unsupported_operation(); |
298 return NULL; |
727 return NULL; |
313 { "flush", (PyCFunction)reader_flush, METH_NOARGS, PyDoc_STR("no-ops") }, |
742 { "flush", (PyCFunction)reader_flush, METH_NOARGS, PyDoc_STR("no-ops") }, |
314 { "isatty", (PyCFunction)reader_isatty, METH_NOARGS, PyDoc_STR("Returns False") }, |
743 { "isatty", (PyCFunction)reader_isatty, METH_NOARGS, PyDoc_STR("Returns False") }, |
315 { "readable", (PyCFunction)reader_readable, METH_NOARGS, |
744 { "readable", (PyCFunction)reader_readable, METH_NOARGS, |
316 PyDoc_STR("Returns True") }, |
745 PyDoc_STR("Returns True") }, |
317 { "read", (PyCFunction)reader_read, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read compressed data") }, |
746 { "read", (PyCFunction)reader_read, METH_VARARGS | METH_KEYWORDS, PyDoc_STR("read compressed data") }, |
|
747 { "read1", (PyCFunction)reader_read1, METH_VARARGS | METH_KEYWORDS, NULL }, |
318 { "readall", (PyCFunction)reader_readall, METH_NOARGS, PyDoc_STR("Not implemented") }, |
748 { "readall", (PyCFunction)reader_readall, METH_NOARGS, PyDoc_STR("Not implemented") }, |
|
749 { "readinto", (PyCFunction)reader_readinto, METH_VARARGS, NULL }, |
|
750 { "readinto1", (PyCFunction)reader_readinto1, METH_VARARGS, NULL }, |
319 { "readline", (PyCFunction)reader_readline, METH_VARARGS, PyDoc_STR("Not implemented") }, |
751 { "readline", (PyCFunction)reader_readline, METH_VARARGS, PyDoc_STR("Not implemented") }, |
320 { "readlines", (PyCFunction)reader_readlines, METH_VARARGS, PyDoc_STR("Not implemented") }, |
752 { "readlines", (PyCFunction)reader_readlines, METH_VARARGS, PyDoc_STR("Not implemented") }, |
321 { "seekable", (PyCFunction)reader_seekable, METH_NOARGS, |
753 { "seekable", (PyCFunction)reader_seekable, METH_NOARGS, |
322 PyDoc_STR("Returns False") }, |
754 PyDoc_STR("Returns False") }, |
323 { "tell", (PyCFunction)reader_tell, METH_NOARGS, |
755 { "tell", (PyCFunction)reader_tell, METH_NOARGS, |