def _complain_ifclosed(closed):
  if closed:
    raise ValueError, "I/O operation on closed file"

class GeneratorFile(object):
  """ File-like object which wraps a Python generator to produce the file contents.
      Modeled on StringIO and comments on the file-like interface copied from there.
  """
  def __init__(self, generator):
    self._generator = generator
    self._closed = False
    self._buf = ''
    self._position = 0

  def __iter__(self):
    return self

  def tell(self):
    """Return the file's current position, like stdio's ftell()."""
    _complain_ifclosed(self._closed)
    return self._position

  def next(self):
    """A file object is its own iterator, for example iter(f) returns f
    (unless f is closed). When a file is used as an iterator, typically
    in a for loop (for example, for line in f: print line), the next()
    method is called repeatedly. This method returns the next input line,
    or raises StopIteration when EOF is hit.
    """
    _complain_ifclosed(self._closed)
    r = self.read()
    if not r:
      raise StopIteration
    return r

  def readline(self):
    buf = []
    while True:
      c = self.read(size=1)
      buf.append(c)
      if c == '\n' or c == '':
        return ''.join(buf)

  def flush(self):
    _complain_ifclosed(self._closed)

  def read(self, size=-1):
    """Read at most size bytes from the file
    (less if the read hits EOF before obtaining size bytes).

    If the size argument is negative or omitted, read all data until EOF
    is reached. The bytes are returned as a string object. An empty
    string is returned when EOF is encountered immediately.
    """
    _complain_ifclosed(self._closed)
    buf = self._buf
    while size < 0 or len(buf) < size:
      try:
        buf = buf + self._generator.next()
      except StopIteration:
        break

    returned = ''
    if size >= 1:
      self._buf = buf[size:]
      returned = buf[:size]
    else:
      self._buf = ''
      returned = buf

    self._position = self._position + len(returned)
    return returned

  def seek(self):
    raise NotImplementedError

  def close(self):
    self._closed = True
    del self._buf

  def __enter__(self):
    return self

  def __exit__(self, type, value, traceback):
    self._closed = True