Skip to content

Commit

Permalink
Merge pull request #373 from tcmitchell/235-copy
Browse files Browse the repository at this point in the history
Migrate `TopLevel.copy` and `SBOLObject.copy` to `sbol3.copy`
  • Loading branch information
tcmitchell authored Feb 8, 2022
2 parents fb71676 + 0f093ad commit dafe580
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 35 deletions.
33 changes: 3 additions & 30 deletions sbol3/object.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ def find(self, search_string: str) -> Optional['SBOLObject']:

def copy(self, target_doc=None, target_namespace=None):

# Delete this method in v1.1
warnings.warn('Use sbol3.copy() instead', DeprecationWarning)

new_uri = self.identity

# If caller specified a target_namespace argument, then copy the object into this
Expand Down Expand Up @@ -143,42 +146,12 @@ def copy(self, target_doc=None, target_namespace=None):


def replace_namespace(old_uri, target_namespace, rdf_type):
"""
Utility function for mapping an SBOL object's identity into a new namespace. The
rdf_type is used to map to and from sbol-typed namespaces.
"""

# Flag as not working to ensure nobody calls this function thinking
# it might do something.
# See https://github.com/SynBioDex/pySBOL3/issues/132
raise NotImplementedError()

# Work around an issue where the Document itself is being copied and
# doesn't have its own URI, so old_uri is None. Return empty string
# because the identity is not allowed to be None.
if old_uri is None:
return ''

# If the value is an SBOL-typed URI, replace both the namespace and class name
class_name = parseClassName(rdf_type)
replacement_target = target_namespace + '/' + class_name

# If not an sbol typed URI, then just replace the namespace
if replacement_target not in old_uri:
replacement_target = target_namespace

if Config.getOption(ConfigOptions.SBOL_TYPED_URIS):
# Map into a typed namespace
replacement = getHomespace() + '/' + class_name
else:
# Map into a non-typed namespace
replacement = getHomespace()

new_uri = old_uri.replace(replacement_target, replacement)
if type(old_uri) is URIRef:
return URIRef(new_uri)
return new_uri


# Global store for builder methods. Custom SBOL classes
# register their builders in this store
Expand Down
3 changes: 3 additions & 0 deletions sbol3/toplevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import posixpath
import urllib.parse
import uuid
import warnings
from typing import Dict, Callable, Optional, Any
import typing

Expand Down Expand Up @@ -171,6 +172,8 @@ def update_all_dependents(self, identity_map: dict[str, Identified]) -> Any:
self.traverse(make_update_references_traverser(identity_map))

def copy(self, target_doc=None, target_namespace=None):
# Delete this method in v1.1
warnings.warn('Use sbol3.copy() instead', DeprecationWarning)
new_obj = super().copy(target_doc=target_doc, target_namespace=target_namespace)
# Need to set `document` on all children recursively. That's what happens when
# you assign to the `document` property of an Identified
Expand Down
15 changes: 13 additions & 2 deletions test/test_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def test_copy_properties(self):
sbol3.set_namespace('https://github.com/synbiodex/pysbol3')
root = sbol3.Component('root', sbol3.SBO_DNA)
root.name = 'foo'
root_copy = root.copy()
objects = sbol3.copy([root])
root_copy = objects[0]
self.assertEqual(root_copy.name, 'foo')

def test_copy_child_objects(self):
Expand All @@ -43,7 +44,8 @@ def test_copy_child_objects(self):
doc.add(sub2)

doc2 = sbol3.Document()
root_copy = root.copy(target_doc=doc2)
objects = sbol3.copy([root], into_document=doc2)
root_copy = objects[0]
self.assertIn(root_copy, doc2.objects)
self.assertEqual([sc.identity for sc in root.features],
[sc.identity for sc in root_copy.features])
Expand All @@ -57,6 +59,15 @@ def test_replace_namespace(self):
from sbol3.object import replace_namespace
replace_namespace(None, None, None)

def test_copy_is_deprecated(self):
namespace = 'https://github.com/synbiodex/pysbol3'
sbol3.set_namespace(namespace)
name = 'ed1'
ed1 = sbol3.ExternallyDefined(types=[sbol3.SBO_DNA],
definition='https://example.org/other')
with self.assertWarns(DeprecationWarning):
ed1.copy()


if __name__ == '__main__':
unittest.main()
11 changes: 9 additions & 2 deletions test/test_toplevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,7 @@ def check_document(i: sbol3.Identified):
'multicellular.nt')
doc = sbol3.Document()
doc.read(test_path)
for obj in doc.objects:
obj.copy(target_doc=dest_doc)
sbol3.copy(doc, into_document=dest_doc)
self.assertEqual(len(doc), len(dest_doc))
for obj in dest_doc.objects:
obj.traverse(check_document)
Expand Down Expand Up @@ -172,6 +171,14 @@ def test_clone_bad_rename(self):
c1_prime = c1.clone(posixpath.join(namespace, clone_name))
self.assertIsNotNone(c1_prime.find('LocalSubComponent2'))

def test_copy_is_deprecated(self):
namespace = 'https://github.com/synbiodex/pysbol3'
sbol3.set_namespace(namespace)
name = 'c1'
c1 = sbol3.Component(name, types=[sbol3.SBO_DNA])
with self.assertWarns(DeprecationWarning):
c1.copy()


if __name__ == '__main__':
unittest.main()
3 changes: 2 additions & 1 deletion test/test_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ def test_shacl_closure_with_toplevels(self):
self.assertEqual(len(doc.validate()), 0)
minidoc = sbol3.Document()
c = doc.find('https://synbiohub.org/public/igem/BBa_I20270')
c.copy(minidoc)
self.assertIsInstance(c, sbol3.TopLevel)
sbol3.copy([c], into_document=minidoc)
# this assertion failed before the fix to the shacl rules
self.assertEqual(len(minidoc.validate()), 0)

Expand Down

0 comments on commit dafe580

Please sign in to comment.