File tree 4 files changed +73
-11
lines changed
4 files changed +73
-11
lines changed Original file line number Diff line number Diff line change @@ -142,12 +142,8 @@ def is_file_like(obj):
142
142
Check if the object is a file-like object.
143
143
144
144
For objects to be considered file-like, they must
145
- be an iterator AND have the following four methods:
146
-
147
- 1) read
148
- 2) write
149
- 3) seek
150
- 4) tell
145
+ be an iterator AND have either a `read` and/or `write`
146
+ method as an attribute.
151
147
152
148
Note: file-like objects must be iterable, but
153
149
iterable objects need not be file-like.
@@ -172,11 +168,8 @@ def is_file_like(obj):
172
168
False
173
169
"""
174
170
175
- file_attrs = ('read' , 'write' , 'seek' , 'tell' )
176
-
177
- for attr in file_attrs :
178
- if not hasattr (obj , attr ):
179
- return False
171
+ if not (hasattr (obj , 'read' ) or hasattr (obj , 'write' )):
172
+ return False
180
173
181
174
if not is_iterator (obj ):
182
175
return False
Original file line number Diff line number Diff line change @@ -100,11 +100,41 @@ def test_is_dict_like():
100
100
101
101
102
102
def test_is_file_like ():
103
+ class MockFile (object ):
104
+ pass
105
+
103
106
is_file = inference .is_file_like
104
107
105
108
data = StringIO ("data" )
106
109
assert is_file (data )
107
110
111
+ # No read / write attributes
112
+ # No iterator attributes
113
+ m = MockFile ()
114
+ assert not is_file (m )
115
+
116
+ MockFile .write = lambda self : 0
117
+
118
+ # Write attribute but not an iterator
119
+ m = MockFile ()
120
+ assert not is_file (m )
121
+
122
+ MockFile .__iter__ = lambda self : self
123
+ MockFile .__next__ = lambda self : 0
124
+ MockFile .next = MockFile .__next__
125
+
126
+ # Valid write-only file
127
+ m = MockFile ()
128
+ assert is_file (m )
129
+
130
+ del MockFile .write
131
+ MockFile .read = lambda self : 0
132
+
133
+ # Valid read-only file
134
+ m = MockFile ()
135
+ assert is_file (m )
136
+
137
+ # Iterator but no read / write attributes
108
138
data = [1 , 2 , 3 ]
109
139
assert not is_file (data )
110
140
Original file line number Diff line number Diff line change @@ -1685,6 +1685,26 @@ class InvalidBuffer(object):
1685
1685
with tm .assert_raises_regex (ValueError , msg ):
1686
1686
self .read_csv (InvalidBuffer ())
1687
1687
1688
+ # gh-16135: we want to ensure that "tell" and "seek"
1689
+ # aren't actually being used when we call `read_csv`
1690
+ #
1691
+ # Thus, while the object may look "invalid" (these
1692
+ # methods are attributes of the `StringIO` class),
1693
+ # it is still a valid file-object for our purposes.
1694
+ class NoSeekTellBuffer (StringIO ):
1695
+ def tell (self ):
1696
+ raise AttributeError ("No tell method" )
1697
+
1698
+ def seek (self , pos , whence = 0 ):
1699
+ raise AttributeError ("No seek method" )
1700
+
1701
+ data = "a\n 1"
1702
+
1703
+ expected = pd .DataFrame ({"a" : [1 ]})
1704
+ result = self .read_csv (NoSeekTellBuffer (data ))
1705
+
1706
+ tm .assert_frame_equal (result , expected )
1707
+
1688
1708
if PY3 :
1689
1709
from unittest import mock
1690
1710
Original file line number Diff line number Diff line change @@ -176,3 +176,22 @@ def test_s3_fails(self):
176
176
# It's irrelevant here that this isn't actually a table.
177
177
with pytest .raises (IOError ):
178
178
read_csv ('s3://cant_get_it/' )
179
+
180
+ @tm .network
181
+ def boto3_client_s3 (self ):
182
+ # see gh-16135
183
+
184
+ # boto3 is a dependency of s3fs
185
+ import boto3
186
+ client = boto3 .client ("s3" )
187
+
188
+ key = "/tips.csv"
189
+ bucket = "pandas-test"
190
+ s3_object = client .get_object (Bucket = bucket , Key = key )
191
+
192
+ result = read_csv (s3_object ["Body" ])
193
+ assert isinstance (result , DataFrame )
194
+ assert not result .empty
195
+
196
+ expected = read_csv (tm .get_data_path ('tips.csv' ))
197
+ tm .assert_frame_equal (result , expected )
You can’t perform that action at this time.
0 commit comments