SynBioDex / pySBOL3

Native python implementation of SBOL 3.0 specification
MIT License
37 stars 16 forks source link

Error when setting identity for SubComponent #410

Open noahsprent opened 2 years ago

noahsprent commented 2 years ago

Not sure if this is me being dense or it's a bug. I'm trying to give an identity to a subcomponent, but I'm receiving an error that I don't understand.

import sbol3
doc = sbol3.Document()
comp = sbol3.Component('Comp', sbol3.SBO_DNA, name="Component")
sub = sbol3.Component('subcomp', sbol3.SBO_DNA, name="Subcomponent")
subcomp = sbol3.SubComponent(sub, identity = "Test")
comp.features += [subcomp]
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [9], in <cell line: 6>()
      4 sub = sbol3.Component('subcomp', sbol3.SBO_DNA, name="Subcomponent")
      5 subcomp = sbol3.SubComponent(sub, identity = "Test")
----> 6 comp.features += [subcomp]

File ~/src/miniconda3/envs/igem-distribution/lib/python3.9/_collections_abc.py:1111, in MutableSequence.__iadd__(self, values)
   1110 def __iadd__(self, values):
-> 1111     self.extend(values)
   1112     return self

File ~/src/miniconda3/envs/igem-distribution/lib/python3.9/_collections_abc.py:1094, in MutableSequence.extend(self, values)
   1092     values = list(values)
   1093 for v in values:
-> 1094     self.append(v)

File ~/src/miniconda3/envs/igem-distribution/lib/python3.9/_collections_abc.py:1073, in MutableSequence.append(self, value)
   1071 def append(self, value):
   1072     'S.append(value) -- append value to the end of the sequence'
-> 1073     self.insert(len(self), value)

File ~/src/miniconda3/envs/igem-distribution/lib/python3.9/site-packages/sbol3/property_base.py:159, in ListProperty.insert(self, index, value)
    157 item = self.from_user(value)
    158 self._storage()[self.property_uri].insert(index, item)
--> 159 self.item_added(value)

File ~/src/miniconda3/envs/igem-distribution/lib/python3.9/site-packages/sbol3/ownedobject.py:52, in OwnedObjectPropertyMixin.item_added(self, item)
     50     if sibling.identity == new_url:
     51         raise ValueError(f'Duplicate URI: {new_url}')
---> 52 item._update_identity(new_url, new_display_id)

File ~/src/miniconda3/envs/igem-distribution/lib/python3.9/site-packages/sbol3/identified.py:192, in Identified._update_identity(self, identity, display_id)
    190     msg = f'{class_name} already has identity {self.identity}'
    191     msg += ' and cannot be re-parented.'
--> 192     raise ValueError(msg)
    193 self._identity = identity
    194 self._display_id = display_id

ValueError: SubComponent already has identity http://sbols.org/unspecified_namespace/Test and cannot be re-parented.

The thing is, that the subcomponent is added and has the correct identity:

>>> comp.features[0].display_id
'Test'

If identity isn't set, the default display_id for the subcomponent is 'SubComponent[n]'. Setting a name works fine:

doc = sbol3.Document()
comp = sbol3.Component('Comp', sbol3.SBO_DNA, name="Component")
sub = sbol3.Component('subcomp', sbol3.SBO_DNA, name="Subcomponent")
subcomp = sbol3.SubComponent(sub,name="Test")
comp.features += [subcomp]
>>> comp.features[0].display_id
'SubComponent1'
>>> comp.features[0].name
'Test'
noahsprent commented 2 years ago

Ah ok I think I see where I've gone wrong here. I shouldn't be giving SubComponent objects identities because they are just pointing to the original component object. I should be using subcomponent.instance_of and then taking the ID from there.

jakebeal commented 2 years ago

In SBOL3, only objects of type TopLevel (e.g., Component, Sequence) get an independent identity, while "child" objects, like SubComponent, gets their identities from their parent objects. For pySBOL3, this happens implicitly as soon as you add the child to the parent. There is a section on it in the docs: https://pysbol3.readthedocs.io/en/stable/getting_started.html#creating-and-adding-child-objects

There is also a helper function in the sbol_utilities.component package called add_feature that captures the whole pattern of making a SubComponent instanceof a component: https://github.com/SynBioDex/SBOL-utilities/blob/8c940eab1e1acc919bc0ea3009b68f4f3e9f092d/sbol_utilities/component.py#L140

noahsprent commented 2 years ago

Thanks! So am I going about it the right way using the .instance_of attribute? Or would it be better to assign names to the subcomponents when I create them and working with those?

jakebeal commented 2 years ago

I think that you want to focus the Component that the instance_of attribute points to: generally we'd show its name, rather than that of the SubComponent that points to it.

An example of this (though in SBOL2 equivalents) is the double terminator B0015: https://synbiohub.org/public/igem/BBa_B0015/1 The name of the first SubComponent there is "component1916610", but what's getting shown on the page is "BBa_B0010", which is the Component that is linked to.