read from the file until a newline is encountered

        .. note::

            this method will block if there isn't already a full line available
            from the data source

        :param max_len: stop reading a single line after this many bytes
        :type max_len: int

        :returns:
            a string of the line it read from the file, including the newline
            at the end

def readline(self, max_len=-1):
        """read from the file until a newline is encountered

        .. note::

            this method will block if there isn't already a full line available
            from the data source

        :param max_len: stop reading a single line after this many bytes
        :type max_len: int

        :returns:
            a string of the line it read from the file, including the newline
            at the end
        """
        buf = self._rbuf
        newline, chunksize = self.NEWLINE, self.CHUNKSIZE
        buf.seek(0)

        text = buf.read()
        if len(text) >= max_len >= 0:
            buf.seek(0)
            buf.truncate()
            buf.write(text[max_len:])
            return text[:max_len]

        while text.find(newline) < 0:
            text = self._read_chunk(chunksize)
            if text is None:
                text = ''
                continue
            if buf.tell() + len(text) >= max_len >= 0:
                text = buf.getvalue() + text
                buf.seek(0)
                buf.truncate()
                buf.write(text[max_len:])
                return text[:max_len]
            if not text:
                break
            buf.write(text)
        else:
            # found a newline
            rc = buf.getvalue()
            index = rc.find(newline) + len(newline)

            buf.seek(0)
            buf.truncate()
            buf.write(rc[index:])
            return rc[:index]

        # hit the end of the file, no more newlines
        rc = buf.getvalue()
        buf.seek(0)
        buf.truncate()
        return rc