def test_namespace(self): base_uri = 'https://github.com/synbiodex/pysbol3' sbol3.set_namespace(base_uri) self.assertEqual(base_uri, sbol3.get_namespace()) # Example from SBOL 3.0 Section 5.1 (page 12) # See issue #80 base_uri = 'https://synbiohub.org' sbol3.set_namespace(base_uri) self.assertEqual(base_uri, sbol3.get_namespace())
def test_default_namespace_with_local_path(self): # Make sure default namespace is honored when the identity has # a local path included test_namespace = 'https://github.com/synbiodex' sbol3.set_namespace(test_namespace) self.assertEqual(test_namespace, sbol3.get_namespace()) identity = posixpath.join(sbol3.get_namespace(), 'pysbol3', 'foo') c = sbol3.Component(identity, types=[sbol3.SBO_DNA]) self.assertEqual(sbol3.get_namespace(), c.namespace) self.assertEqual('foo', c.display_id) self.assertEqual(identity, c.identity) self.assertEqual(0, len(c.validate()))
def test_identity_display_id(self): # Test setting of display_id # * Test by passing display_id to constructor # * Test by having display_id deduced from identity sbol3.set_namespace('https://github.com/synbiodex/pysbol3') c1_display_id = 'c1' c1_identity = posixpath.join(sbol3.get_namespace(), c1_display_id) c1 = sbol3.Component(c1_display_id, sbol3.SBO_DNA) self.assertEqual(c1_display_id, c1.display_id) self.assertEqual(c1_identity, c1.identity) # Now test identity and display_id from a URL-type URI c2_display_id = 'c2' c2_identity = posixpath.join(sbol3.get_namespace(), c2_display_id) c2 = sbol3.Component(c2_identity, sbol3.SBO_DNA) self.assertEqual(c2_display_id, c2.display_id) self.assertEqual(c2_identity, c2.identity)
def test_no_namespace(self): # Make sure there is no default namespace self.assertEqual(None, sbol3.get_namespace()) # Make sure that creating an object with a display_id # and no default namespace raises an exception with self.assertRaises(sbol3.NamespaceError): c = sbol3.Component('c1', sbol3.SBO_DNA)
def test_cloning(self): sbol3.set_namespace('https://github.com/synbiodex/pysbol3') c1 = sbol3.Component('c1', sbol3.SBO_DNA) new_identity = 'c2' c2 = c1.clone(new_identity) self.assertEqual(posixpath.join(sbol3.get_namespace(), new_identity), c2.identity)
def test_trailing_slash(self): # A trailing slash on an object's identity should automatically be removed sbol3.set_namespace('http://example.org/sbol3') slash_identity = posixpath.join(sbol3.get_namespace(), 'c1', '') self.assertTrue(slash_identity.endswith(posixpath.sep)) c = sbol3.Component(slash_identity, sbol3.SBO_DNA) identity = slash_identity.strip(posixpath.sep) self.assertEqual(identity, c.identity)
def test_namespace_mismatch_uuid(self): # Now check a UUID with no default namespace set # sbol3.set_namespace(None) self.assertIsNone(sbol3.get_namespace()) c = sbol3.Component(uuid.uuid4().urn, types=[sbol3.SBO_DNA]) report = c.validate() self.assertIsNotNone(report) # Expecting at least one error self.assertEqual(0, len(report))
def test_cloning_with_references(self): sbol3.set_namespace('https://github.com/synbiodex/pysbol3') c1 = sbol3.Component('c1', sbol3.SBO_DNA) new_identity = 'c2' seq1 = sbol3.Sequence('s1') c1.sequences.append(seq1) c2 = c1.clone(new_identity) self.assertEqual(posixpath.join(sbol3.get_namespace(), new_identity), c2.identity) self.assertListEqual(list(c1.sequences), list(c2.sequences))
def test_no_namespace(self): # This test requires that no objects have been created without # a namespace set. The warning only triggers on the first such # creation. There is probably a way to reset the triggering of # warnings if we need that in the future. # # Make sure there is no default namespace self.assertEqual(None, sbol3.get_namespace()) # Make sure creation without a namespace generates a warning display_id = 'c1' with self.assertWarns(UserWarning) as cm: c = sbol3.Component(display_id, sbol3.SBO_DNA) # We expect the warning to come from object.py. Update this if # the logic changes. self.assertIn('object.py', cm.filename) # We expect the default namespace to appear in the identity self.assertIn(sbol3.PYSBOL3_DEFAULT_NAMESPACE, c.identity) # We expect the default namespace at the beginning of the identity self.assertTrue(c.identity.startswith(sbol3.PYSBOL3_DEFAULT_NAMESPACE)) # We expect the display_id to appear in the identity self.assertIn(display_id, c.identity) # We expect the display_id to be at the end of the identity self.assertTrue(c.identity.endswith(display_id))
def row_to_basic_part(doc: sbol3.Document, row, basic_parts: sbol3.Collection, linear_products: sbol3.Collection, final_products: sbol3.Collection, config: dict, source_table: dict): """ Read a row for a basic part and turn it into SBOL Component :param doc: Document to add parts to :param row: Excel row to be processed :param basic_parts: collection of parts to add to :param linear_products: collection of linear parts to add to :param final_products: collection of final parts to add to :param config: dictionary of sheet parsing configuration variables :param source_table: dictionary mapping source names to namespaces :return: None """ # Parse material from sheet row name = row[config['basic_name_col']].value if name is None: return # skip lines without names else: name = name.strip() # make sure we're discarding whitespace raw_role = row[config['basic_role_col']].value try: # look up with tyto; if fail, leave blank or add to description role = (tyto.SO.get_uri_by_term(raw_role) if raw_role else None) except LookupError: logging.warning( f'Role "{raw_role}" could not be found in Sequence Ontology') role = None design_notes = (row[config['basic_notes_col']].value if row[config['basic_notes_col']].value else "") description = (row[config['basic_description_col']].value if row[config['basic_description_col']].value else "") source_prefix = row[config['basic_source_prefix_col']].value source_id = row[config['basic_source_id_col']].value final_product = row[config['basic_final_col']].value # boolean circular = row[config['basic_circular_col']].value # boolean length = row[config['basic_length_col']].value raw_sequence = row[config['basic_sequence_col']].value sequence = (None if raw_sequence is None else "".join( unicodedata.normalize("NFKD", raw_sequence).upper().split())) if not ((sequence is None and length == 0) or len(sequence) == length): raise ValueError( f'Part "{name}" has mismatched sequence length: check for bad characters and extra whitespace' ) # identity comes from source if set to a literal table, from display_id if not set identity = None display_id = None was_derived_from = None namespace = sbol3.get_namespace() if source_id and source_prefix: source_prefix = source_prefix.strip() if source_prefix in source_table: if source_table[source_prefix]: display_id = sbol3.string_to_display_id(source_id.strip()) identity = f'{source_table[source_prefix]}/{display_id}' namespace = source_table[source_prefix] else: # when there is no prefix, use the bare value (in SBOL3 format) raw_url = source_id.strip() identity = url_to_identity( strip_filetype_suffix(strip_sbol2_version(raw_url))) was_derived_from = raw_url namespace = identity.rsplit( '/', 1)[0] # TODO: use a helper function else: logging.info( f'Part "{name}" ignoring non-literal source: {source_prefix}') elif source_id: logging.warning( f'Part "{name}" has source ID specified but not prefix: {source_id}' ) elif source_prefix: logging.warning( f'Part "{name}" has source prefix specified but not ID: {source_prefix}' ) if not identity: display_id = sbol3.string_to_display_id(name) # build a component from the material logging.debug(f'Creating basic part "{name}"') component = sbol3.Component( identity or display_id, sbol3.SBO_DNA, name=name, namespace=namespace, description=f'{design_notes}\n{description}'.strip()) if was_derived_from: component.derived_from.append(was_derived_from) doc.add(component) if role: component.roles.append(role) if circular: component.types.append(sbol3.SO_CIRCULAR) if sequence: sbol_seq = sbol3.Sequence(f'{component.identity}_sequence', namespace=namespace, encoding=sbol3.IUPAC_DNA_ENCODING, elements=sequence) doc.add(sbol_seq) component.sequences.append(sbol_seq.identity) # add the component to the appropriate collections basic_parts.members.append(component.identity) if final_product: linear_products.members.append(component.identity) final_products.members.append(component.identity)