From 19975ddf4f747f4b47fffcb069dbec0a9ae7dda3 Mon Sep 17 00:00:00 2001 From: David Doty Date: Fri, 25 Aug 2023 15:39:48 -0700 Subject: [PATCH] added unit test for internal modification that goes between bases --- scadnano/scadnano.py | 10 ++++++++-- tests/scadnano_tests.py | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/scadnano/scadnano.py b/scadnano/scadnano.py index 1a04f2b..e6b2bcb 100644 --- a/scadnano/scadnano.py +++ b/scadnano/scadnano.py @@ -1123,11 +1123,17 @@ class ModificationInternal(Modification): """Internal modification of DNA sequence, e.g., biotin or Cy3.""" allowed_bases: Optional[AbstractSet[str]] = None - """If None, then this is an internal modification that goes between bases. + """ + If None, then this is an internal modification that goes between bases. + In this case, the key :data:`Strand.modifications_int` specifying the position of the internal + modification is interpreted to mean that the modification goes *after* the base at that position. + (For example, this is the parameter `idx` in :meth:`StrandBuilder.with_modification_internal`.) + If instead it is a list of bases, then this is an internal modification that attaches to a base, and this lists the allowed bases for this internal modification to be placed at. For example, internal biotins for IDT must be at a T. If any base is allowed, it should be - ``{'A','C','G','T'}``.""" + ``{'A','C','G','T'}``. + """ def __post_init__(self) -> None: super().__post_init__() diff --git a/tests/scadnano_tests.py b/tests/scadnano_tests.py index 96613ba..96aaf0d 100644 --- a/tests/scadnano_tests.py +++ b/tests/scadnano_tests.py @@ -1136,6 +1136,29 @@ def test_domain_delimiters_modifications(self) -> None: self.assertEqual(f'{strand_name};/5Biosg/ AAAAA CCCC/iBiodT/ GGGGG /3Cy3Sp/;25nm;STD', idt_content) + def test_domain_delimiters_internal_nonbase_modifications(self) -> None: + strand_name = 's1' + mod_i = sc.ModificationInternal(display_text='9C', idt_text='/iSp9/') + + helices = [sc.Helix(max_offset=100) for _ in range(6)] + design = sc.Design(helices=helices, strands=[], grid=sc.square) + + (design.draw_strand(0, 0) + .move(5).with_domain_sequence('AAAAA') + .cross(1).move(-5).with_domain_sequence('CCCCT') + .cross(2).move(5).with_domain_sequence('GGGGG') + .with_name(strand_name) + .with_modification_internal(8, mod_i) + ) + + strand = design.strands[0] + strand_idt_dna_sequence = strand.idt_dna_sequence(domain_delimiter=' ') + self.assertEqual('AAAAA CCCC/iSp9/T GGGGG', strand_idt_dna_sequence) + + idt_content = design.to_idt_bulk_input_format(delimiter=';', domain_delimiter=' ') + self.assertEqual(f'{strand_name};AAAAA CCCC/iSp9/T GGGGG;25nm;STD', + idt_content) + def test_to_idt_bulk_input_format__row_major_5p(self) -> None: key = sc.strand_order_key_function(column_major=False, strand_order=sc.StrandOrder.five_prime) names_joined = self._get_names_idt(self.design_6h, key)