Skip to content

Commit 524b522

Browse files
pitrouwesm
authored andcommitted
ARROW-2218: [Python] PythonFile should infer mode when not given
Author: Antoine Pitrou <antoine@python.org> Closes apache#1674 from pitrou/ARROW-2218-infer-python-file-mode and squashes the following commits: a5d704e <Antoine Pitrou> ARROW-2218: PythonFile should infer mode when not given
1 parent d3fabe0 commit 524b522

File tree

4 files changed

+53
-3
lines changed

4 files changed

+53
-3
lines changed

python/pyarrow/io-hdfs.pxi

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,5 @@ cdef class HdfsFile(NativeFile):
480480
object mode
481481
object parent
482482

483-
cdef object __weakref__
484-
485483
def __dealloc__(self):
486484
self.parent = None

python/pyarrow/io.pxi

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,9 +410,21 @@ cdef class PythonFile(NativeFile):
410410
cdef:
411411
object handle
412412

413-
def __cinit__(self, handle, mode='w'):
413+
def __cinit__(self, handle, mode=None):
414414
self.handle = handle
415415

416+
if mode is None:
417+
try:
418+
mode = handle.mode
419+
except AttributeError:
420+
# Not all file-like objects have a mode attribute
421+
# (e.g. BytesIO)
422+
try:
423+
mode = 'w' if handle.writable() else 'r'
424+
except AttributeError:
425+
raise ValueError("could not infer open mode for file-like "
426+
"object %r, please pass it explicitly"
427+
% (handle,))
416428
if mode.startswith('w'):
417429
self.wr_file.reset(new PyOutputStream(handle))
418430
self.is_writable = True

python/pyarrow/lib.pxd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,7 @@ cdef class NativeFile:
337337
bint is_writable
338338
readonly bint closed
339339
bint own_file
340+
object __weakref__
340341

341342
# By implementing these "virtual" functions (all functions in Cython
342343
# extension classes are technically virtual in the C++ sense) we can expose

python/pyarrow/tests/test_io.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import os
2222
import pytest
2323
import sys
24+
import weakref
2425

2526
import numpy as np
2627

@@ -124,6 +125,44 @@ def get_buffer():
124125
assert buf.to_pybytes() == b'sample'
125126
assert buf.parent is not None
126127

128+
129+
def test_python_file_implicit_mode(tmpdir):
130+
path = os.path.join(str(tmpdir), 'foo.txt')
131+
with open(path, 'wb') as f:
132+
pf = pa.PythonFile(f)
133+
assert pf.writable()
134+
assert not pf.readable()
135+
assert not pf.seekable() # PyOutputStream isn't seekable
136+
f.write(b'foobar\n')
137+
138+
with open(path, 'rb') as f:
139+
pf = pa.PythonFile(f)
140+
assert pf.readable()
141+
assert not pf.writable()
142+
assert pf.seekable()
143+
assert pf.read() == b'foobar\n'
144+
145+
bio = BytesIO()
146+
pf = pa.PythonFile(bio)
147+
assert pf.writable()
148+
assert not pf.readable()
149+
assert not pf.seekable()
150+
pf.write(b'foobar\n')
151+
assert bio.getvalue() == b'foobar\n'
152+
153+
154+
def test_python_file_closing():
155+
bio = BytesIO()
156+
pf = pa.PythonFile(bio)
157+
wr = weakref.ref(pf)
158+
del pf
159+
assert wr() is None # object was destroyed
160+
assert not bio.closed
161+
pf = pa.PythonFile(bio)
162+
pf.close()
163+
assert bio.closed
164+
165+
127166
# ----------------------------------------------------------------------
128167
# Buffers
129168

0 commit comments

Comments
 (0)