def readComponents(streamOrString, validate=False, transform=True,
                   ignoreUnreadable=False, allowQP=False):
    """
    Generate one Component at a time from a stream.
    """
    if isinstance(streamOrString, basestring):
        stream = six.StringIO(streamOrString)
    else:
        stream = streamOrString

    try:
        stack = Stack()
        versionLine = None
        n = 0
        for line, n in getLogicalLines(stream, allowQP):
            if ignoreUnreadable:
                try:
                    vline = textLineToContentLine(line, n)
                except VObjectError as e:
                    if e.lineNumber is not None:
                        msg = "Skipped line {lineNumber}, message: {msg}"
                    else:
                        msg = "Skipped a line, message: {msg}"
                    logger.error(msg.format(**{'lineNumber': e.lineNumber, 'msg': str(e)}))
                    continue
            else:
                vline = textLineToContentLine(line, n)
            if vline.name == "VERSION":
                versionLine = vline
                stack.modifyTop(vline)
            elif vline.name == "BEGIN":
                stack.push(Component(vline.value, group=vline.group))
            elif vline.name == "PROFILE":
                if not stack.top():
                    stack.push(Component())
                stack.top().setProfile(vline.value)
            elif vline.name == "END":
                if len(stack) == 0:
                    err = "Attempted to end the {0} component but it was never opened"
                    raise ParseError(err.format(vline.value), n)

                if vline.value.upper() == stack.topName():  # START matches END
                    if len(stack) == 1:
                        component = stack.pop()
                        if versionLine is not None:
                            component.setBehaviorFromVersionLine(versionLine)
                        else:
                            behavior = getBehavior(component.name)
                            if behavior:
                                component.setBehavior(behavior)
                        if validate:
                            component.validate(raiseException=True)
                        if transform:
                            component.transformChildrenToNative()
                        yield component  # EXIT POINT
                    else:
                        stack.modifyTop(stack.pop())
                else:
                    err = "{0} component wasn't closed"
                    raise ParseError(err.format(stack.topName()), n)
            else:
                stack.modifyTop(vline)  # not a START or END line
        if stack.top():
            if stack.topName() is None:
                logger.warning("Top level component was never named")
            elif stack.top().useBegin:
                raise ParseError("Component {0!s} was never closed".format(
                                 (stack.topName())), n)
            yield stack.pop()

    except ParseError as e:
        e.input = streamOrString
        raise
Generate one Component at a time from a stream.