|
13 | 13 | import numpy as np
|
14 | 14 |
|
15 | 15 | from pandas import compat
|
16 |
| -from pandas.compat import (range, lrange, StringIO, lzip, |
| 16 | +from pandas.compat import (range, lrange, PY3, StringIO, lzip, |
17 | 17 | zip, string_types, map, u)
|
18 | 18 | from pandas.core.dtypes.common import (
|
19 | 19 | is_integer, _ensure_object,
|
|
31 | 31 | from pandas.core.common import AbstractMethodError
|
32 | 32 | from pandas.io.date_converters import generic_parser
|
33 | 33 | from pandas.errors import ParserWarning, ParserError, EmptyDataError
|
34 |
| -from pandas.io.common import (get_filepath_or_buffer, _validate_header_arg, |
35 |
| - _get_handle, UnicodeReader, UTF8Recoder, |
36 |
| - BaseIterator, |
37 |
| - _NA_VALUES, _infer_compression) |
| 34 | +from pandas.io.common import (get_filepath_or_buffer, is_file_like, |
| 35 | + _validate_header_arg, _get_handle, |
| 36 | + UnicodeReader, UTF8Recoder, _NA_VALUES, |
| 37 | + BaseIterator, _infer_compression) |
38 | 38 | from pandas.core.tools import datetimes as tools
|
39 | 39 |
|
40 | 40 | from pandas.util._decorators import Appender
|
@@ -755,7 +755,9 @@ def __init__(self, f, engine=None, **kwds):
|
755 | 755 | self.squeeze = options.pop('squeeze', False)
|
756 | 756 |
|
757 | 757 | # might mutate self.engine
|
| 758 | + self.engine = self._check_file_or_buffer(f, engine) |
758 | 759 | self.options, self.engine = self._clean_options(options, engine)
|
| 760 | + |
759 | 761 | if 'has_index_names' in kwds:
|
760 | 762 | self.options['has_index_names'] = kwds['has_index_names']
|
761 | 763 |
|
@@ -801,6 +803,23 @@ def _get_options_with_defaults(self, engine):
|
801 | 803 |
|
802 | 804 | return options
|
803 | 805 |
|
| 806 | + def _check_file_or_buffer(self, f, engine): |
| 807 | + # see gh-16530 |
| 808 | + if is_file_like(f): |
| 809 | + next_attr = "__next__" if PY3 else "next" |
| 810 | + |
| 811 | + # The C engine doesn't need the file-like to have the "next" or |
| 812 | + # "__next__" attribute. However, the Python engine explicitly calls |
| 813 | + # "next(...)" when iterating through such an object, meaning it |
| 814 | + # needs to have that attribute ("next" for Python 2.x, "__next__" |
| 815 | + # for Python 3.x) |
| 816 | + if engine != "c" and not hasattr(f, next_attr): |
| 817 | + msg = ("The 'python' engine cannot iterate " |
| 818 | + "through this file buffer.") |
| 819 | + raise ValueError(msg) |
| 820 | + |
| 821 | + return engine |
| 822 | + |
804 | 823 | def _clean_options(self, options, engine):
|
805 | 824 | result = options.copy()
|
806 | 825 |
|
|
0 commit comments