From e0ac2dcfaf4fd0f78b7d6bd6f5cc55eac06c3451 Mon Sep 17 00:00:00 2001 From: Tom Mitchell Date: Thu, 18 Jun 2020 15:56:15 -0400 Subject: [PATCH 1/3] Add optional validation to Document.write() --- sbol2/document.py | 11 +++++++++++ test/test_document.py | 32 ++++++++++++++++++++++++++++++++ test/test_roundtrip.py | 3 +++ 3 files changed, 46 insertions(+) diff --git a/sbol2/document.py b/sbol2/document.py index 91279eb..9027c12 100644 --- a/sbol2/document.py +++ b/sbol2/document.py @@ -2,6 +2,7 @@ import logging import os import posixpath +import time from typing import Any, Dict, Mapping, Union import warnings @@ -395,6 +396,16 @@ def write(self, filename): or empty string if validation is disabled. """ self.doc_serialize_rdf2xml(filename) + # Optionally validate + result = 'Validation disabled. To enable use of the online validation tool, use' + result += ' Config.setOption(ConfigOptions.VALIDATE, True)' + if Config.getOption(ConfigOptions.VALIDATE): + t_start = time.time() + result = self.validate() + if Config.getOption(ConfigOptions.VERBOSE): + t_end = time.time() + print(f'Validation request took {t_end - t_start} seconds') + return result def read(self, filename): """ diff --git a/test/test_document.py b/test/test_document.py index 05acdd9..4de5666 100644 --- a/test/test_document.py +++ b/test/test_document.py @@ -1,6 +1,9 @@ +import io import locale import os +import tempfile import unittest +import unittest.mock import rdflib @@ -544,6 +547,35 @@ def test_idempotent_read(self): cd = doc.componentDefinitions[cd_uri] self.assertEqual(1, len(cd.roles)) + def test_write_validation(self): + # Test that write performs validation if requested + # and skips validation if requested. + doc = sbol2.Document() + doc.moduleDefinitions.create('md1') + validate = sbol2.Config.getOption(sbol2.ConfigOptions.VALIDATE) + sbol2.Config.setOption(sbol2.ConfigOptions.VALIDATE, True) + verbose = sbol2.Config.getOption(sbol2.ConfigOptions.VERBOSE) + sbol2.Config.setOption(sbol2.ConfigOptions.VERBOSE, True) + with tempfile.TemporaryDirectory() as tmpdirname: + test_file = os.path.join(tmpdirname, 'test.xml') + with unittest.mock.patch('sys.stdout', new=io.StringIO()) as fake_out: + # Write to disk + result = doc.write(test_file) + self.assertEqual('Valid.', result) + # Expect timing output + output = fake_out.getvalue().strip() + self.assertTrue(output.startswith('Validation request took')) + self.assertTrue(output.endswith('seconds')) + sbol2.Config.setOption(sbol2.ConfigOptions.VALIDATE, False) + with tempfile.TemporaryDirectory() as tmpdirname: + test_file = os.path.join(tmpdirname, 'test.xml') + # Write to disk + result = doc.write(test_file) + self.assertTrue(result.startswith('Validation disabled.')) + # Reset validate to its original value + sbol2.Config.setOption(sbol2.ConfigOptions.VALIDATE, validate) + sbol2.Config.setOption(sbol2.ConfigOptions.VERBOSE, verbose) + if __name__ == '__main__': unittest.main() diff --git a/test/test_roundtrip.py b/test/test_roundtrip.py index 9aef5df..3aba5a7 100644 --- a/test/test_roundtrip.py +++ b/test/test_roundtrip.py @@ -19,6 +19,9 @@ class TestRoundTripSBOL2(unittest.TestCase): def setUp(self): # Create temp directory self.temp_out_dir = tempfile.mkdtemp() + # Disable validation to avoid unnecessary round trips + # to the online validator + sbol2.Config.setOption(sbol2.ConfigOptions.VALIDATE, False) self.logger = logging.getLogger('sbol2.test') if not self.logger.hasHandlers(): logging.basicConfig() From 5d65a8ea234300bb675c2f6fa3ffb8f78dabc65e Mon Sep 17 00:00:00 2001 From: Tom Mitchell Date: Thu, 18 Jun 2020 15:59:40 -0400 Subject: [PATCH 2/3] Clean up test_sequence.py --- test/test_sequence.py | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/test/test_sequence.py b/test/test_sequence.py index b3922ec..2c66a5a 100644 --- a/test/test_sequence.py +++ b/test/test_sequence.py @@ -1,11 +1,8 @@ import os -import sys import tempfile import unittest -import rdflib - -import sbol2 as sbol +import sbol2 MODULE_LOCATION = os.path.dirname(os.path.abspath(__file__)) CRISPR_EXAMPLE = os.path.join(MODULE_LOCATION, 'resources', 'crispr_example.xml') @@ -14,23 +11,23 @@ class TestSequence(unittest.TestCase): def testAddSequence(self): - test_seq = sbol.Sequence("R0010", "ggctgca") - doc = sbol.Document() + test_seq = sbol2.Sequence("R0010", "ggctgca") + doc = sbol2.Document() doc.addSequence(test_seq) seq = doc.sequences.get("R0010").elements self.assertEqual(seq, 'ggctgca') def testRemoveSequence(self): - test_seq = sbol.Sequence("R0010", "ggctgca") - doc = sbol.Document() + test_seq = sbol2.Sequence("R0010", "ggctgca") + doc = sbol2.Document() doc.addSequence(test_seq) doc.sequences.remove(0) - with self.assertRaises(sbol.SBOLError): + with self.assertRaises(sbol2.SBOLError): doc.sequences.get("R0010") def testSeqDisplayId(self): - doc = sbol.Document() + doc = sbol2.Document() doc.read(CRISPR_EXAMPLE) # List of displayIds @@ -41,15 +38,15 @@ def testSeqDisplayId(self): self.assertCountEqual(listseq_read, listseq) def testSequenceEncoding(self): - doc = sbol.Document() + doc = sbol2.Document() doc.read(CRISPR_EXAMPLE) seq = doc.sequences.get('CRP_b_seq') - self.assertEqual(seq.encoding, sbol.SBOL_ENCODING_IUPAC) + self.assertEqual(seq.encoding, sbol2.SBOL_ENCODING_IUPAC) def testSequenceElement(self): - sbol.setHomespace('http://sbols.org/CRISPR_Example') - sbol.Config.setOption('sbol_typed_uris', False) - doc = sbol.Document() + sbol2.setHomespace('http://sbols.org/CRISPR_Example') + sbol2.Config.setOption('sbol_typed_uris', False) + doc = sbol2.Document() doc.read(CRISPR_EXAMPLE) # Sequence to test against seq = ('GCTCCGAATTTCTCGACAGATCTCATGTGATTACGCCAAGCTACGGGCGGAGTACTGTCCTC' @@ -62,9 +59,9 @@ def testSequenceElement(self): self.assertEqual(seq_read, seq) def testUpdateSequenceElement(self): - sbol.setHomespace('http://sbols.org/CRISPR_Example') - sbol.Config.setOption('sbol_typed_uris', False) - doc = sbol.Document() + sbol2.setHomespace('http://sbols.org/CRISPR_Example') + sbol2.Config.setOption('sbol_typed_uris', False) + doc = sbol2.Document() doc.read(CRISPR_EXAMPLE) # Sequence to test against seq = 'AAAAA' @@ -74,14 +71,14 @@ def testUpdateSequenceElement(self): # File I/O Tests def testUpdateWrite(self): - sbol.setHomespace('http://sbols.org/CRISPR_Example') - sbol.Config.setOption('sbol_typed_uris', False) - doc = sbol.Document() + sbol2.setHomespace('http://sbols.org/CRISPR_Example') + sbol2.Config.setOption('sbol_typed_uris', False) + doc = sbol2.Document() doc.read(CRISPR_EXAMPLE) # Sequence to test against seq = 'AAAAA' doc.sequences.get('CRP_b_seq').elements = seq - doc2 = sbol.Document() # Document to compare for equality + doc2 = sbol2.Document() # Document to compare for equality with tempfile.TemporaryDirectory() as tmpdirname: test_file = os.path.join(tmpdirname, 'test.xml') # Write to disk @@ -92,5 +89,5 @@ def testUpdateWrite(self): self.assertEqual(seq_read, seq) def test_bool(self): - seq = sbol.Sequence() + seq = sbol2.Sequence() self.assertTrue(seq) From 7fa485f9b5aa1b1129027c4a71a0a608e23a4ee0 Mon Sep 17 00:00:00 2001 From: Tom Mitchell Date: Thu, 18 Jun 2020 16:00:13 -0400 Subject: [PATCH 3/3] Add examples directory to pycodestyle checks --- dev/hooks/pre-commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/hooks/pre-commit b/dev/hooks/pre-commit index b7c0883..576e510 100755 --- a/dev/hooks/pre-commit +++ b/dev/hooks/pre-commit @@ -16,7 +16,7 @@ fi # Run style checker MAX_VIOLATIONS=23 echo "Checking style..." -pycodestyle sbol2 test > style.txt +pycodestyle sbol2 test examples > style.txt result=$(cat style.txt | wc -l) if [ "$result" -gt $MAX_VIOLATIONS ]; then cat <