def annotate_residues_from_sequence(molecule, attribute, sequence):
    """
    Sets the attribute `attribute` to a value from `sequence` for every node in
    `molecule`. Nodes in the n'th residue of `molecule` are given the n'th
    value of `sequence`.

    Parameters
    ----------
    molecule: networkx.Graph
        The molecule to annotate. Is modified in-place.
    attribute: collections.abc.Hashable
        The attribute to set.
    sequence: collections.abc.Sequence
        The values assigned.

    Raises
    ------
    ValueError
        If the length of `sequence` is different from the number of residues in
        `molecule`.
    """
    residues = list(molecule.iter_residues())
    if len(sequence) == 1:
        sequence = sequence * len(residues)
    elif len(sequence) != len(residues):
        msg = (
            'The sequence length does not match the number of residues. The sequence has {} elements for {} residues.'
            )
        raise ValueError(msg.format(len(sequence), len(residues)))
    for residue_nodes, value in zip(residues, sequence):
        for node_name in residue_nodes:
            molecule.nodes[node_name][attribute] = value
