diff --git a/pysoundfile.py b/pysoundfile.py index dc330b8..1be4bce 100644 --- a/pysoundfile.py +++ b/pysoundfile.py @@ -356,21 +356,14 @@ def __init__(self, file, mode='r', sample_rate=None, channels=None, self._file = _snd.sf_open(file, mode_int, self._info) elif isinstance(file, int): self._file = _snd.sf_open_fd(file, mode_int, self._info, closefd) - else: - # Note: readinto() is not checked - for attr in ('seek', 'read', 'write', 'tell'): - if not hasattr(file, attr): - raise RuntimeError( - "file must be a filename, a file descriptor or " - "a file-like object with the methods " - "'seek()', 'read()', 'write()' and 'tell()'") - # Note: the callback functions in _vio must be kept alive! - self._vio = self._init_vio(file) - vio = _ffi.new("SF_VIRTUAL_IO*", self._vio) - self._file = _snd.sf_open_virtual(vio, mode_int, self._info, - _ffi.NULL) + elif all(hasattr(file, a) for a in ('seek', 'read', 'write', 'tell')): + self._file = _snd.sf_open_virtual( + self._init_virtual_io(file), mode_int, self._info, _ffi.NULL) self._name = str(file) - + else: + raise RuntimeError("file must be a filename, a file descriptor or " + "a file-like object with the methods " + "'seek()', 'read()', 'write()' and 'tell()'") self._handle_error() name = property(lambda self: self._name) @@ -397,26 +390,23 @@ def __init__(self, file, mode='r', sample_rate=None, channels=None, # avoid confusion if something goes wrong before assigning self._file: _file = None - def _init_vio(self, file): + def _init_virtual_io(self, file): @_ffi.callback("sf_vio_get_filelen") def vio_get_filelen(user_data): - # Streams must set _length or implement __len__ - if hasattr(file, '_length'): - size = file._length - elif not hasattr(file, '__len__'): - old_file_position = file.tell() + # first try __len__(), if not available fall back to seek()/tell() + try: + size = len(file) + except TypeError: + curr = file.tell() file.seek(0, SEEK_END) size = file.tell() - file.seek(old_file_position, SEEK_SET) - else: - size = len(file) + file.seek(curr, SEEK_SET) return size @_ffi.callback("sf_vio_seek") def vio_seek(offset, whence, user_data): file.seek(offset, whence) - curr = file.tell() - return curr + return file.tell() @_ffi.callback("sf_vio_read") def vio_read(ptr, count, user_data): @@ -435,18 +425,20 @@ def vio_read(ptr, count, user_data): def vio_write(ptr, count, user_data): buf = _ffi.buffer(ptr, count) data = buf[:] - length = file.write(data) - return length + return file.write(data) @_ffi.callback("sf_vio_tell") def vio_tell(user_data): return file.tell() - return {'get_filelen': vio_get_filelen, - 'seek': vio_seek, - 'read': vio_read, - 'write': vio_write, - 'tell': vio_tell} + # Note: the callback functions must be kept alive! + self._virtual_io = {'get_filelen': vio_get_filelen, + 'seek': vio_seek, + 'read': vio_read, + 'write': vio_write, + 'tell': vio_tell} + + return _ffi.new("SF_VIRTUAL_IO*", self._virtual_io) def __del__(self): self.close()