old_contents: # -*- coding: utf-8 -*-
  
  from __future__ import absolute_import
  
  import io
  import os
  
  from blox.utils import read_i64, write_i64, read_json, write_json
  from blox.blosc import write_blosc
  
  
  class File(object):
      def __init__(self, filename, mode=None):
          filename = os.path.abspath(os.path.expanduser(filename))
          if mode is None:
              mode = 'r' if os.path.exists(filename) else 'w'
          elif mode not in 'rw':
              raise ValueError('invalid mode: {!r}; expected r/w'.format(mode))
          self._mode = mode
          self._filename = filename
          self._handle = io.open(filename, mode + 'b')
          self._index = {}
          if not self.writable:
              try:
                  self._handle.seek(-8, os.SEEK_END)
                  self._handle.seek(read_i64(self._handle))
                  self._index = read_json(self._handle)
              except Exception as e:
                  raise IOError('cannot read file ({}: {})'.format(type(e).__name__, str(e)))
          self._seek = 0
  
      @property
      def filename(self):
          return self._filename
  
      @property
      def mode(self):
          return self._mode
  
      @property
      def writable(self):
          return self._mode == 'w'
  
      @property
      def filesize(self):
          return os.stat(self._filename).st_size
  
      def _write_index(self):
          self._handle.truncate()
          write_json(self._handle, self._index)
          write_i64(self._handle, self._seek)
  
      def create_dataset(self, name, data, compression='lz4', level=5, shuffle=True):
          if not self.writable:
              raise ValueError('file is not writable')
          if name in self._index:
              raise ValueError('dataset {!r} already exists'.format(name))
          self._index[name] = self._seek
          self._handle.seek(self._seek)
          self._seek = write_blosc(data, self._handle, compression, level, shuffle)
          self._write_index()
  
new_contents: # -*- coding: utf-8 -*-
  
  from __future__ import absolute_import
  
  import io
  import os
  
  from blox.utils import read_i64, write_i64, read_json, write_json
  from blox.blosc import write_blosc
  
  
  class File(object):
      def __init__(self, filename, mode=None):
          filename = os.path.abspath(os.path.expanduser(filename))
          if mode is None:
              mode = 'r' if os.path.exists(filename) else 'w'
          elif mode not in 'rw':
              raise ValueError('invalid mode: {!r}; expected r/w'.format(mode))
          self._mode = mode
          self._filename = filename
          self._handle = io.open(filename, mode + 'b')
          self._index = {}
          self._dirty = True
          if not self.writable:
              try:
                  self._handle.seek(-8, os.SEEK_END)
                  self._handle.seek(read_i64(self._handle))
                  self._index = read_json(self._handle)
              except Exception as e:
                  raise IOError('cannot read file ({}: {})'.format(type(e).__name__, str(e)))
          self._seek = 0
  
      @property
      def filename(self):
          return self._filename
  
      @property
      def mode(self):
          return self._mode
  
      @property
      def writable(self):
          return self._mode == 'w'
  
      @property
      def filesize(self):
          return os.stat(self._filename).st_size
  
      def _write_index(self):
          self._handle.truncate()
          write_json(self._handle, self._index)
          write_i64(self._handle, self._seek)
          self._dirty = False
  
      def close(self):
          if self._handle is not None:
              if self.writable:
                  if self._dirty:
                      self._write_index()
                  self._handle.flush()
              self._handle.close()
          self._handle = None
  
      def create_dataset(self, name, data, compression='lz4', level=5, shuffle=True):
          if not self.writable:
              raise ValueError('file is not writable')
          if name in self._index:
              raise ValueError('dataset {!r} already exists'.format(name))
          self._index[name] = self._seek
          self._handle.seek(self._seek)
          self._dirty = True
          self._seek = write_blosc(data, self._handle, compression, level, shuffle)
          self._write_index()
  
subject: Add File#close method; maintain dirty state for the file