Commit 70388794 authored by Maximilian Beckers's avatar Maximilian Beckers
Browse files

new section of ECDF in tutorial

parent c287d6ad
......@@ -415,12 +415,12 @@ def checkNormality(map, windowSize, boxCoord):
#get maximum distances between ECDF and CDF
ECDFvals, sampleSort = estimateECDFFromMap(map, windowSize, boxCoord);
KSstat, Dn, Dn_tail, n = KolmogorowSmirnow(ECDFvals, sampleSort);
output = "Maximum Distance Dn between ECDF and CDF: Dn=" + repr(Dn) + ", in Tail: " + repr(Dn_tail) + ". Sample size used: " + repr(n);
output = "Maximum Distance Dn between ECDF and CDF: Dn=" + " %.4f" %Dn + ", in Tail:" + " %.4f" %Dn_tail + ". Sample size used: " + repr(n);
print(output);
#do Anderson-Darling test for normality
AnDarl, pVal, n = AndersonDarling(sampleSort);
output = "Anderson-Darling test summary: " + repr(AnDarl) + ". p-Value: " + repr(pVal) + ". Sample size used: " + repr(n);
output = "Anderson-Darling test summary: " + repr(AnDarl) + ". p-Value: " + "%.4f" %pVal + ". Sample size used: " + repr(n);
if pVal != -1.0:
print(output);
else:
......
......@@ -188,10 +188,20 @@ def calculate_scaled_map(emmap, modmap, mask, wn, wn_locscale, apix, locFilt, lo
#prepare windows of particle for scaling
frequencyMap_mapWindow = FDRutil.calculate_frequency_map(np.zeros((wn_locscale, wn_locscale, wn_locscale)));
numSteps = len(xrange(0, sizeMap[0] - int(wn_locscale), stepSize))*len(xrange(0, sizeMap[1] - int(wn_locscale), stepSize))*len(xrange(0, sizeMap[2] - int(wn_locscale), stepSize));
counterSteps = 0;
for k in xrange(0, sizeMap[0] - int(wn_locscale), stepSize):
for j in xrange(0, sizeMap[1] - int(wn_locscale), stepSize):
for i in xrange(0, sizeMap[2] - int(wn_locscale), stepSize):
#print progress
counterSteps = counterSteps + 1;
progress = counterSteps/float(numSteps);
if counterSteps%(int(numSteps/20.0)) == 0:
output = "%.1f" %(progress*100) + "% finished ..." ;
print(output);
#crop windows
emmap_wn = emmap[k: k + wn_locscale, j: j + wn_locscale, i: i + wn_locscale];
modmap_wn = modmap[k: k + wn_locscale, j: j + wn_locscale, i: i + wn_locscale];
......@@ -242,7 +252,7 @@ def calculate_scaled_map(emmap, modmap, mask, wn, wn_locscale, apix, locFilt, lo
if ecdfBool:
tmpECDF, sampleSort = FDRutil.estimateECDFFromMap(map_noise_sharpened_data, -1, -1);
ecdf = np.interp(map_b_sharpened[central_pix, central_pix, central_pix], sampleSort, tmpECDF, left=0.0, right=1.0);
ecdf = np.interp(map_b_sharpened, sampleSort, tmpECDF, left=0.0, right=1.0);
else:
ecdf = 0;
......@@ -257,7 +267,7 @@ def calculate_scaled_map(emmap, modmap, mask, wn, wn_locscale, apix, locFilt, lo
sharpened_map[k + halfStep : k + halfStep + stepSize, j + halfStep : j + halfStep + stepSize, i + halfStep : i + halfStep + stepSize] = np.copy(map_b_sharpened[halfStep:halfStep+stepSize, halfStep:halfStep+stepSize, halfStep:halfStep+stepSize]);
sharpened_mean_vals[k + halfStep : k + halfStep + stepSize, j + halfStep : j + halfStep + stepSize, i + halfStep : i + halfStep + stepSize] = mean;
sharpened_var_vals[k + halfStep : k + halfStep + stepSize, j + halfStep : j + halfStep + stepSize, i + halfStep : i + halfStep + stepSize] = var;
sharpened_ecdf_vals[k + halfStep : k + halfStep + stepSize, j + halfStep : j + halfStep + stepSize, i + halfStep : i + halfStep + stepSize] = ecdf;
sharpened_ecdf_vals[k + halfStep : k + halfStep + stepSize, j + halfStep : j + halfStep + stepSize, i + halfStep : i + halfStep + stepSize] = ecdf[halfStep:halfStep+stepSize, halfStep:halfStep+stepSize, halfStep:halfStep+stepSize];
return sharpened_map, sharpened_mean_vals, sharpened_var_vals, sharpened_ecdf_vals;
......
Copyright (c) 2016, Science and Technology Facilities Council
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
......@@ -14,8 +14,10 @@ Functions
* :func:`new`: Create a new MRC file.
* :func:`open`: Open an MRC file.
* :func:`open_async`: Open an MRC file asynchronously.
* :func:`mmap`: Open a memory-mapped MRC file (fast for large files).
* :func:`validate`: Validate an MRC file (not implemented yet!)
* :func:`new_mmap`: Create a new empty memory-mapped MRC file (fast for large files).
* :func:`validate`: Validate an MRC file
Basic usage
-----------
......@@ -61,6 +63,6 @@ http://www.ccpem.ac.uk/mrc_format/mrc2014.php
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from .load_functions import new, open, mmap
from .load_functions import new, open, open_async, mmap, new_mmap
from .validator import validate
from .version import __version__
......@@ -43,10 +43,10 @@ class Bzip2MrcFile(MrcFile):
open(name, mode='w').close()
self._iostream = bz2.BZ2File(name, mode='r')
def _read(self):
def _read(self, header_only=False):
"""Override _read() to ensure bzip2 file is in read mode."""
self._ensure_readable_stream()
super(Bzip2MrcFile, self)._read()
super(Bzip2MrcFile, self)._read(header_only)
def _ensure_readable_stream(self):
"""Make sure _iostream is a bzip2 stream that can be read."""
......@@ -57,6 +57,16 @@ class Bzip2MrcFile(MrcFile):
"""Override _get_file_size() to avoid seeking from end."""
self._ensure_readable_stream()
return super(Bzip2MrcFile, self)._get_file_size()
def _read_bytearray_from_stream(self, number_of_bytes):
"""Override because BZ2File in Python 2 does not support :meth:`~io.BufferedIOBase.readinto`."""
if hasattr(self._iostream, "readinto"):
# Python 3 - BZ2File supports ``readinto()`` so we just use the normal implementation
return super(Bzip2MrcFile, self)._read_bytearray_from_stream(number_of_bytes)
else:
# Python 2 - need to read as bytes then copy to a bytearray
result_bytes = self._iostream.read(number_of_bytes)
return bytearray(result_bytes), len(result_bytes)
def flush(self):
"""Override :meth:`~mrcfile.mrcinterpreter.MrcInterpreter.flush` since
......
......@@ -40,5 +40,5 @@ def print_headers(names=None, print_file=None):
if names is None:
names = sys.argv[1:]
for name in names:
with mrcfile.open(name, permissive=True) as mrc:
with mrcfile.open(name, permissive=True, header_only=True) as mrc:
mrc.print_header(print_file=print_file)
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
# Copyright (c) 2018, Science and Technology Facilities Council
# This software is distributed under a BSD licence. See LICENSE.txt.
"""
future_mrcfile
--------------
Module which exports the :class:`FutureMrcFile` class.
Classes:
:class:`FutureMrcFile`: An object which represents an MRC file being
opened asynchronously.
"""
# Import Python 3 features for future-proofing
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import threading
class FutureMrcFile(object):
"""Object representing an MRC file being opened asynchronously.
This API deliberately mimics a :class:`~concurrent.futures.Future` object
from the :mod:`concurrent.futures` module in Python 3.2+ (which we do
not use directly because this code still needs to run in Python 2.7).
"""
def __init__(self, open_function, args=(), kwargs={}):
"""Initialise a new :class:`FutureMrcFile` object.
This constructor starts a new thread which will invoke the callable
given in ``open_function`` with the given arguments.
Args:
open_function: The callable to use to open the MRC file. (This will
normally be :func:`mrcfile.open`, but could also be
:class:`~mrcfile.mrcfile.MrcFile` or any of its subclasses.)
args: A tuple of positional arguments to use when ``open_function``
is called. (Normally a 1-tuple containing the name of the file
to open.)
kwargs: A dictionary of keyword arguments to use when
``open_function`` is called.
"""
self._result_holder = [None]
self._open_function = open_function
self._thread = threading.Thread(target=self._run,
args=args,
kwargs=kwargs)
self._thread.start()
def _run(self, *args, **kwargs):
"""Call the open function and store the result in the holder list.
(For internal use only.)
"""
try:
mrc = self._open_function(*args, **kwargs)
self._result_holder[0] = mrc
except Exception as ex:
self._result_holder[0] = ex
def cancel(self):
"""Return :data:`False`.
(See :meth:`concurrent.futures.Future.cancel` for more details. This
implementation does not allow jobs to be cancelled.)
"""
return False
def cancelled(self):
"""Return :data:`False`.
(See :meth:`concurrent.futures.Future.cancelled` for more details.
This implementation does not allow jobs to be cancelled.)
"""
return False
def running(self):
"""Return :data:`True` if the :class:`~mrcfile.mrcfile.MrcFile` is
currently being opened.
(See :meth:`concurrent.futures.Future.running` for more details.)
"""
return self._thread.is_alive()
def done(self):
"""Return :data:`True` if the file opening has finished.
(See :meth:`concurrent.futures.Future.done` for more details.)
"""
return not self.running()
def result(self, timeout=None):
"""Return the :class:`~mrcfile.mrcfile.MrcFile` that has been opened.
(See :meth:`concurrent.futures.Future.result` for more details.)
Args:
timeout: Time to wait (in seconds) for the file opening to finish.
If ``timeout`` is not specified or is :data:`None`, there is no
limit to the wait time.
Returns:
An :class:`~mrcfile.mrcfile.MrcFile` object (or one of its
subclasses).
Raises:
:exc:`RuntimeError`: If the operation has not finished within the
time limit set by ``timeout``. (Note that the type of this
exception will change in future if this class is replaced by
:class:`concurrent.futures.Future`.)
:exc:`Exception`: Any exception raised by the
:class:`~mrcfile.mrcfile.MrcFile` opening operation will be
re-raised here.
"""
result = self._get_result(timeout)
if isinstance(result, Exception):
raise result
else:
return result
def exception(self, timeout=None):
"""Return the exception raised by the file opening operation.
(See :meth:`concurrent.futures.Future.exception` for more details.)
Args:
timeout: Time to wait (in seconds) for the operation to finish. If
``timeout`` is not specified or is :data:`None`, there is no
limit to the wait time.
Returns:
An :exc:`Exception`, if one was raised by the file opening
operation, or :data:`None` if no exception was raised.
Raises:
:exc:`RuntimeError`: If the operation has not finished within the
time limit set by ``timeout``. (Note that the type of this
exception will change in future if this class is replaced by
:class:`concurrent.futures.Future`.)
"""
result = self._get_result(timeout)
if isinstance(result, Exception):
return result
else:
return None
def _get_result(self, timeout):
"""Return the result or exception from the file opening operation.
(For internal use only.)
"""
self._thread.join(timeout=timeout)
if self._thread.is_alive():
raise RuntimeError('Timed out waiting for result')
return self._result_holder[0]
def add_done_callback(self, fn):
"""Not implemented.
(See :meth:`concurrent.futures.Future.add_done_callback` for more details.)
"""
raise NotImplementedError
......@@ -43,10 +43,10 @@ class GzipMrcFile(MrcFile):
self._iostream.close()
self._fileobj.close()
def _read(self):
def _read(self, header_only=False):
"""Override _read() to ensure gzip file is in read mode."""
self._ensure_readable_gzip_stream()
super(GzipMrcFile, self)._read()
super(GzipMrcFile, self)._read(header_only)
def _ensure_readable_gzip_stream(self):
"""Make sure _iostream is a gzip stream that can be read."""
......
......@@ -19,9 +19,11 @@ import os
from .bzip2mrcfile import Bzip2MrcFile
from .constants import MAP_ID, MAP_ID_OFFSET_BYTES
from .future_mrcfile import FutureMrcFile
from .gzipmrcfile import GzipMrcFile
from .mrcfile import MrcFile
from .mrcmemmap import MrcMemmap
from . import utils
def new(name, data=None, compression=None, overwrite=False):
......@@ -47,8 +49,9 @@ def new(name, data=None, compression=None, overwrite=False):
subclass of it if ``compression`` is specified).
Raises:
:class:`~exceptions.ValueError`: If the compression format is not
recognised.
:exc:`ValueError`: If the file already exists and overwrite is
:data:`False`.
:exc:`ValueError`: If the compression format is not recognised.
"""
if compression == 'gzip':
NewMrc = GzipMrcFile
......@@ -65,7 +68,7 @@ def new(name, data=None, compression=None, overwrite=False):
return mrc
def open(name, mode='r', permissive=False): # @ReservedAssignment
def open(name, mode='r', permissive=False, header_only=False): # @ReservedAssignment
"""Open an MRC file.
This function opens both normal and compressed MRC files. Supported
......@@ -77,7 +80,7 @@ def open(name, mode='r', permissive=False): # @ReservedAssignment
This function offers a permissive read mode for attempting to open corrupt
or invalid files. In permissive mode, :mod:`warnings` are issued instead of
exceptions if problems with the file are encountered. See
:class:`mrcfile.mrcinterpreter.MrcInterpreter` or the
:class:`~mrcfile.mrcinterpreter.MrcInterpreter` or the
:doc:`usage guide <../usage_guide>` for more information.
Args:
......@@ -87,6 +90,8 @@ def open(name, mode='r', permissive=False): # @ReservedAssignment
file. The default is ``r``.
permissive: Read the file in permissive mode. The default is
:data:`False`.
header_only: Only read the header (and extended header) from the file.
The default is :data:`False`.
Returns:
An :class:`~mrcfile.mrcfile.MrcFile` object (or a
......@@ -94,21 +99,23 @@ def open(name, mode='r', permissive=False): # @ReservedAssignment
gzipped).
Raises:
:class:`~exceptions.ValueError`: If the mode is not one of ``r``,
``r+`` or ``w+``.
:class:`~exceptions.ValueError`: If the file is not a valid MRC file
and ``permissive`` is :data:`False`.
:class:`~exceptions.ValueError`: If the mode is ``w+`` and the file
already exists. (Call :func:`new` with ``overwrite=True`` to
deliberately overwrite an existing file.)
:class:`~exceptions.OSError`: If the mode is ``r`` or ``r+`` and the
file does not exist.
:exc:`ValueError`: If the mode is not one of ``r``, ``r+`` or ``w+``.
:exc:`ValueError`: If the file is not a valid MRC file and
``permissive`` is :data:`False`.
:exc:`ValueError`: If the mode is ``w+`` and the file already exists.
(Call :func:`new` with ``overwrite=True`` to deliberately overwrite
an existing file.)
:exc:`OSError`: If the mode is ``r`` or ``r+`` and the file does not
exist.
Warns:
RuntimeWarning: If the file appears to be a valid MRC file but the data
block is longer than expected from the dimensions in the header.
RuntimeWarning: If the file is not a valid MRC file and ``permissive``
is :data:`True`.
RuntimeWarning: If the header's ``exttyp`` field is set to a known
value but the extended header's size is not a multiple of the
number of bytes in the corresponding dtype.
"""
NewMrc = MrcFile
if os.path.exists(name):
......@@ -125,7 +132,50 @@ def open(name, mode='r', permissive=False): # @ReservedAssignment
NewMrc = GzipMrcFile
elif start[:2] == b'BZ':
NewMrc = Bzip2MrcFile
return NewMrc(name, mode=mode, permissive=permissive)
return NewMrc(name, mode=mode, permissive=permissive,
header_only=header_only)
def open_async(name, mode='r', permissive=False):
"""Open an MRC file asynchronously in a separate thread.
This allows a file to be opened in the background while the main thread
continues with other work. This can be a good way to improve performance if
the main thread is busy with intensive computation, but will be less
effective if the main thread is itself busy with disk I/O.
Multiple files can be opened in the background simultaneously. However,
this implementation is relatively crude; each call to this function will
start a new thread and immediately use it to start opening a file. If you
try to open many large files at the same time, performance will decrease as
all of the threads attempt to access the disk at once. You'll also risk
running out of memory to store the data from all the files.
This function returns a :class:`~mrcfile.future_mrcfile.FutureMrcFile`
object, which deliberately mimics the API of the
:class:`~concurrent.futures.Future` object from Python 3's
:mod:`concurrent.futures` module. (Future versions of this library might
return genuine :class:`~concurrent.futures.Future` objects instead.)
To get the real :class:`~mrcfile.mrcfile.MrcFile` object from a
:class:`~mrcfile.future_mrcfile.FutureMrcFile`, call
:meth:`~mrcfile.future_mrcfile.FutureMrcFile.result`. This will block until
the file has been read and the :class:`~mrcfile.mrcfile.MrcFile` object is
ready. To check if the :class:`~mrcfile.mrcfile.MrcFile` is ready without
blocking, call :meth:`~mrcfile.future_mrcfile.FutureMrcFile.running` or
:meth:`~mrcfile.future_mrcfile.FutureMrcFile.done`.
Args:
name: The file name to open.
mode: The file mode (one of ``r``, ``r+`` or ``w+``).
permissive: Read the file in permissive mode. The default is
:data:`False`.
Returns:
A :class:`~mrcfile.future_mrcfile.FutureMrcFile` object.
"""
return FutureMrcFile(open, (name,), dict(mode=mode, permissive=permissive))
def mmap(name, mode='r', permissive=False):
......@@ -136,9 +186,11 @@ def mmap(name, mode='r', permissive=False):
the :class:`~mrcfile.mrcmemmap.MrcMemmap` class documentation for more
information.
In all other ways, :func:`mmap` behaves in exactly the same way as
:func:`open`. The :class:`~mrcfile.mrcmemmap.MrcMemmap` object returned by
this function can be used in exactly the same way as a normal
Because the memory-mapped data array accesses the disk directly, compressed
files cannot be opened with this function. In all other ways, :func:`mmap`
behaves in exactly the same way as :func:`open`. The
:class:`~mrcfile.mrcmemmap.MrcMemmap` object returned by this function can
be used in exactly the same way as a normal
:class:`~mrcfile.mrcfile.MrcFile` object.
Args:
......@@ -151,3 +203,59 @@ def mmap(name, mode='r', permissive=False):
An :class:`~mrcfile.mrcmemmap.MrcMemmap` object.
"""
return MrcMemmap(name, mode=mode, permissive=permissive)
def new_mmap(name, shape, mrc_mode=0, fill=None, overwrite=False):
"""Create a new, empty memory-mapped MRC file.
This function is useful for creating very large files. The initial contents
of the data array can be set with the ``fill`` parameter if needed, but be
aware that filling a large array can take a long time.
If ``fill`` is not set, the new data array's contents are unspecified and
system-dependent. (Some systems fill a new empty mmap with zeros, others
fill it with the bytes from the disk at the newly-mapped location.) If you
are definitely going to fill the entire array with new data anyway you can
safely leave ``fill`` as :data:`None`, otherwise it is advised to use a
sensible fill value (or ensure you are on a system that fills new mmaps
with a reasonable default value).
Args:
name: The file name to use.
shape: The shape of the data array to open, as a 2-, 3- or 4-tuple of
ints. For example, ``(nz, ny, nx)`` for a new 3D volume, or
``(ny, nx)`` for a new 2D image.
mrc_mode: The MRC mode to use for the new file. One of 0, 1, 2, 4 or 6,
which correspond to numpy dtypes as follows:
* mode 0 -> int8
* mode 1 -> int16
* mode 2 -> float32
* mode 4 -> complex64
* mode 6 -> uint16
The default is 0.
fill: An optional value to use to fill the new data array. If
:data:`None`, the data array will not be filled and its contents
are unspecified. Numpy's usual rules for rounding or rejecting
values apply, according to the dtype of the array.
overwrite: Flag to force overwriting of an existing file. If
:data:`False` and a file of the same name already exists, the file
is not overwritten and an exception is raised.
Returns:
A new :class:`~mrcfile.mrcmemmap.MrcMemmap` object.
Raises:
:exc:`ValueError`: If the MRC mode is invalid.
:exc:`ValueError`: If the file already exists and overwrite is
:data:`False`.
"""
mrc = MrcMemmap(name, mode='w+', overwrite=overwrite)
dtype = utils.dtype_from_mode(mrc_mode)
mrc._open_memmap(dtype, shape)
mrc.update_header_from_data()
if fill is not None:
mrc.data[...] = fill
mrc.flush()
return mrc
......@@ -44,13 +44,13 @@ class MrcFile(MrcInterpreter):
In mode ``r`` or ``r+``, the named file is opened from disk and read.
In mode ``w+`` a new empty file is created and will be written to disk
at the end of the :keyword:`with` block (or when :meth:`flush` or
:meth:`close` is called).
at the end of the :keyword:`with` block (or when
:meth:`~.MrcInterpreter.flush` or :meth:`close` is called).
"""
def __init__(self, name, mode='r', overwrite=False, permissive=False,
**kwargs):
header_only=False, **kwargs):
"""Initialise a new :class:`MrcFile` object.
The given file name is opened in the given mode. For mode ``r`` or
......@@ -70,19 +70,28 @@ class MrcFile(MrcInterpreter):
permissive: Read the file in permissive mode. (See
:class:`mrcfile.mrcinterpreter.MrcInterpreter` for details.)
The default is :data:`False`.
header_only: Only read the header (and extended header) from the
file. The default is :data:`False`.
Raises:
:class:`~exceptions.ValueError`: If the mode is not one of ``r``,
``r+`` or ``w+``, the file is not a valid MRC file, or if the
mode is ``w+``, the file already exists and overwrite is
:data:`False`.
:class:`~exceptions.OSError`: If the mode is ``r`` or ``r+`` and
the file does not exist.
:exc:`ValueError`: If the mode is not one of ``r``, ``r+`` or
``w+``.
:exc:`ValueError`: If the file is not a valid MRC file and
``permissive`` is :data:`False`.
:exc:`ValueError`: If the mode is ``w+``, the file already exists
and overwrite is :data:`False`.
:exc:`OSError`: If the mode is ``r`` or ``r+`` and the file does
not exist.
Warns:
RuntimeWarning: The file appears to be a valid MRC file but the
RuntimeWarning: If the file appears to be a valid MRC file but the
data block is longer than expected from the dimensions in the
header.
RuntimeWarning: If the file is not a valid MRC file and
``permissive`` is :data:`True`.
RuntimeWarning: If the header's ``exttyp`` field is set to a known
value but the extended header's size is not a multiple of the
number of bytes in the corresponding dtype.
"""
super(MrcFile, self).__init__(permissive=permissive, **kwargs)
......@@ -102,7 +111,7 @@ class MrcFile(MrcInterpreter):
if 'w' in mode:
self._create_default_attributes()
else:
self._read()
self._read(header_only)
except Exception:
self._close_file()
raise
......@@ -115,10 +124,10 @@ class MrcFile(MrcInterpreter):
"""Open a file object to use as the I/O stream."""
self._iostream = open(name, self._mode + 'b')
def _read(self):
def _read(self, header_only=False):
"""Override _read() to move back to start of file first."""
self._iostream.seek(0)
super(MrcFile, self)._read()
super(MrcFile, self)._read(header_only)
# Check if the file is the expected size.
if self.data is not None:
......@@ -143,8 +152,8 @@ class MrcFile(MrcInterpreter):
def close(self):
"""Flush any changes to disk and close the file.
This override calls :meth:`super().close` to ensure the stream is
flushed and closed, then closes the file object.
This override calls :meth:`.MrcInterpreter.close` to ensure the stream
is flushed and closed, then closes the file object.
"""
super(MrcFile, self).close()
self._close_file()
......