def add_sample(self, sample: Sample) -> None:
<EOL>if sample.Sample_ID is None:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>if len(self.samples) == <NUM_LIT:0>:<EOL><INDENT>self.samples_have_index = sample.index is not None<EOL>self.samples_have_index2 = sample.index2 is not None<EOL><DEDENT>if (<EOL>len(self.samples) == <NUM_LIT:0><EOL>and sample.Read_Structure is not None<EOL>and self.Read_Structure is None<EOL>):<EOL><INDENT>if (<EOL>self.is_paired_end<EOL>and not sample.Read_Structure.is_paired_end<EOL>or self.is_single_end  <EOL>and not sample.Read_Structure.is_single_end<EOL>):<EOL><INDENT>raise ValueError(<EOL>f'<STR_LIT>'<EOL>f'<STR_LIT>'<EOL>f'<STR_LIT>'<EOL>)<EOL><DEDENT>self.Read_Structure = sample.Read_Structure.copy()<EOL><DEDENT>if self.Read_Structure != sample.Read_Structure:<EOL><INDENT>raise ValueError(<EOL>f'<STR_LIT>'<EOL>f'<STR_LIT>'<EOL>)<EOL><DEDENT>for other in self.samples:<EOL><INDENT>if sample == other:<EOL><INDENT>message = (<EOL>f'<STR_LIT>'<EOL>f'<STR_LIT>'<EOL>)<EOL>warnings.warn(UserWarning(message))<EOL><DEDENT>if sample.index is None and self.samples_have_index:<EOL><INDENT>raise ValueError(<EOL>f'<STR_LIT>'<EOL>f'<STR_LIT>'<EOL>)<EOL><DEDENT>if sample.index2 is None and self.samples_have_index2:<EOL><INDENT>raise ValueError(<EOL>f'<STR_LIT>'<EOL>f'<STR_LIT>'<EOL>)<EOL><DEDENT>if (<EOL>self.samples_have_index<EOL>and self.samples_have_index2<EOL>and sample.index == other.index<EOL>and sample.index2 == other.index2<EOL>and sample.Lane == other.Lane<EOL>):<EOL><INDENT>raise ValueError(<EOL>f'<STR_LIT>'<EOL>f'<STR_LIT>'<EOL>)<EOL><DEDENT>if (<EOL>self.samples_have_index<EOL>and not self.samples_have_index2<EOL>and sample.index == other.index<EOL>and sample.Lane == other.Lane<EOL>):<EOL><INDENT>raise ValueError(<EOL>f'<STR_LIT>'<EOL>f'<STR_LIT>'<EOL>)<EOL><DEDENT>if (<EOL>not self.samples_have_index<EOL>and self.samples_have_index2<EOL>and sample.index2 == other.index2<EOL>and sample.Lane == other.Lane<EOL>):<EOL><INDENT>raise ValueError(<EOL>f'<STR_LIT>'<EOL>f'<STR_LIT>'<EOL>)<EOL><DEDENT><DEDENT>sample.sample_sheet = self<EOL>self._samples.append(sample)<EOL>
Add a :class:`Sample` to this :class:`SampleSheet`.

        All samples are validated against the first sample added to the sample
        sheet to ensure there are no ID collisions or incompatible read
        structures (if supplied). All samples are also validated against the
        ``"[Reads]"`` section of the sample sheet if it has been defined.

        The following validation is performed when adding a sample:

            - :class:`Read_Structure` is identical in all samples, if supplied
            - :class:`Read_Structure` is compatible with ``"[Reads]"``, if \
                supplied
            - Samples on the same ``"Lane"`` cannot have the same \
                ``"Sample_ID"`` and ``"Library_ID"``.
            - Samples cannot have the same ``"Sample_ID"`` if no ``"Lane"`` \
                has been defined.
            - The same ``"index"`` or ``"index2"`` combination cannot exist \
                per flowcell or per lane if lanes have been defined.
            - All samples have the same index design (``"index"``, \
                ``"index2"``) per flowcell or per lane if lanes have been \
                defined.

        Args:
            sample: :class:`Sample` to add to this :class:`SampleSheet`.

        Note:

            It is unclear if the Illumina specification truly allows for
            equivalent samples to exist on the same sample sheet. To mitigate
            the warnings in this library when you encounter such a case, use
            a code pattern like the following:

            >>> import warnings
            >>> warnings.simplefilter("ignore")
            >>> from sample_sheet import SampleSheet
            >>> SampleSheet('tests/resources/single-end-colliding-sample-ids.csv');
            SampleSheet('tests/resources/single-end-colliding-sample-ids.csv')