This repository has been archived by the owner on Oct 28, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
288 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
""" | ||
Hack for getting a handle to the top-level google module, etc. | ||
""" | ||
from __future__ import division | ||
from __future__ import print_function | ||
from __future__ import unicode_literals | ||
|
||
import google.protobuf.json_format as json_format | ||
import google.protobuf.message as message | ||
import google.protobuf.struct_pb2 as struct_pb2 | ||
|
||
|
||
def getJsonFormat(): | ||
""" | ||
Returns the module google.protobuf.json_format | ||
""" | ||
return json_format | ||
|
||
|
||
def getMessage(): | ||
""" | ||
Returns the module google.protobuf.message | ||
""" | ||
return message | ||
|
||
|
||
def getStructPb2(): | ||
""" | ||
Returns the module google.protobuf.struct_pb2 | ||
""" | ||
return struct_pb2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
""" | ||
Definitions of the GA4GH protocol types. | ||
""" | ||
from __future__ import division | ||
from __future__ import print_function | ||
from __future__ import unicode_literals | ||
|
||
import datetime | ||
import json | ||
import inspect | ||
import sys | ||
|
||
from _protocol_version import version # noqa | ||
from ga4gh.common_pb2 import * # noqa | ||
from ga4gh.assay_metadata_pb2 import * # noqa | ||
from ga4gh.metadata_pb2 import * # noqa | ||
from ga4gh.metadata_service_pb2 import * # noqa | ||
from ga4gh.read_service_pb2 import * # noqa | ||
from ga4gh.reads_pb2 import * # noqa | ||
from ga4gh.reference_service_pb2 import * # noqa | ||
from ga4gh.references_pb2 import * # noqa | ||
from ga4gh.variant_service_pb2 import * # noqa | ||
from ga4gh.variants_pb2 import * # noqa | ||
from ga4gh.allele_annotations_pb2 import * # noqa | ||
from ga4gh.allele_annotation_service_pb2 import * # noqa | ||
from ga4gh.sequence_annotations_pb2 import * # noqa | ||
from ga4gh.sequence_annotation_service_pb2 import * # noqa | ||
from ga4gh.bio_metadata_pb2 import * # noqa | ||
from ga4gh.bio_metadata_service_pb2 import * # noqa | ||
from ga4gh.genotype_phenotype_pb2 import * # noqa | ||
from ga4gh.genotype_phenotype_service_pb2 import * # noqa | ||
from ga4gh.rna_quantification_pb2 import * # noqa | ||
from ga4gh.rna_quantification_service_pb2 import * # noqa | ||
|
||
import hacks.googhack as googhack | ||
|
||
|
||
# This is necessary because we have a package in the same directory as this | ||
# file named 'google', so an 'import google' attempts to import that package | ||
# instead of the top-level google package. | ||
json_format = googhack.getJsonFormat() | ||
message = googhack.getMessage() | ||
struct_pb2 = googhack.getStructPb2() | ||
|
||
|
||
def getValueListName(protocolResponseClass): | ||
""" | ||
Returns the name of the attribute in the specified protocol class | ||
that is used to hold the values in a search response. | ||
""" | ||
return protocolResponseClass.DESCRIPTOR.fields_by_number[1].name | ||
|
||
|
||
def convertDatetime(t): | ||
""" | ||
Converts the specified datetime object into its appropriate protocol | ||
value. This is the number of milliseconds from the epoch. | ||
""" | ||
epoch = datetime.datetime.utcfromtimestamp(0) | ||
delta = t - epoch | ||
millis = delta.total_seconds() * 1000 | ||
return int(millis) | ||
|
||
|
||
def getValueFromValue(value): | ||
""" | ||
Extract the currently set field from a Value structure | ||
""" | ||
if type(value) != struct_pb2.Value: | ||
raise TypeError("Expected a Value, but got {}".format(type(value))) | ||
if value.WhichOneof("kind") is None: | ||
raise AttributeError("Nothing set for {}".format(value)) | ||
return getattr(value, value.WhichOneof("kind")) | ||
|
||
|
||
def toJson(protoObject, indent=None): | ||
""" | ||
Serialises a protobuf object as json | ||
""" | ||
# Using the internal method because this way we can reformat the JSON | ||
js = json_format.MessageToDict(protoObject, False) | ||
return json.dumps(js, indent=indent) | ||
|
||
|
||
def toJsonDict(protoObject): | ||
""" | ||
Converts a protobuf object to the raw attributes | ||
i.e. a key/value dictionary | ||
""" | ||
return json.loads(toJson(protoObject)) | ||
|
||
|
||
def fromJson(json, protoClass): | ||
""" | ||
Deserialise json into an instance of protobuf class | ||
""" | ||
return json_format.Parse(json, protoClass()) | ||
|
||
|
||
def validate(json, protoClass): | ||
""" | ||
Check that json represents data that could be used to make | ||
a given protobuf class | ||
""" | ||
try: | ||
fromJson(json, protoClass) | ||
# The json conversion automatically validates | ||
return True | ||
except Exception: | ||
return False | ||
|
||
|
||
def getProtocolClasses(superclass=message.Message): | ||
""" | ||
Returns all the protocol classes that are subclasses of the | ||
specified superclass. Only 'leaf' classes are returned, | ||
corresponding directly to the classes defined in the protocol. | ||
""" | ||
# We keep a manual list of the superclasses that we define here | ||
# so we can filter them out when we're getting the protocol | ||
# classes. | ||
superclasses = set([message.Message]) | ||
thisModule = sys.modules[__name__] | ||
subclasses = [] | ||
for name, class_ in inspect.getmembers(thisModule): | ||
if ((inspect.isclass(class_) and | ||
issubclass(class_, superclass) and | ||
class_ not in superclasses)): | ||
subclasses.append(class_) | ||
return subclasses | ||
|
||
|
||
postMethods = \ | ||
[('/callsets/search', | ||
SearchCallSetsRequest, # noqa | ||
SearchCallSetsResponse), # noqa | ||
('/datasets/search', | ||
SearchDatasetsRequest, # noqa | ||
SearchDatasetsResponse), # noqa | ||
('/readgroupsets/search', | ||
SearchReadGroupSetsRequest, # noqa | ||
SearchReadGroupSetsResponse), # noqa | ||
('/reads/search', | ||
SearchReadsRequest, # noqa | ||
SearchReadsResponse), # noqa | ||
('/references/search', | ||
SearchReferencesRequest, # noqa | ||
SearchReferencesResponse), # noqa | ||
('/referencesets/search', | ||
SearchReferenceSetsRequest, # noqa | ||
SearchReferenceSetsResponse), # noqa | ||
('/variants/search', | ||
SearchVariantsRequest, # noqa | ||
SearchVariantsResponse), # noqa | ||
('/datasets/search', | ||
SearchDatasetsRequest, # noqa | ||
SearchDatasetsResponse), # noqa | ||
('/callsets/search', | ||
SearchCallSetsRequest, # noqa | ||
SearchCallSetsResponse), # noqa | ||
('/featuresets/search', | ||
SearchFeatureSetsRequest, # noqa | ||
SearchFeatureSetsResponse), # noqa | ||
('/features/search', | ||
SearchFeaturesRequest, # noqa | ||
SearchFeaturesResponse), # noqa | ||
('/variantsets/search', | ||
SearchVariantSetsRequest, # noqa | ||
SearchVariantSetsResponse), # noqa | ||
('/variantannotations/search', | ||
SearchVariantAnnotationsRequest, # noqa | ||
SearchVariantAnnotationSetsResponse), # noqa | ||
('/variantannotationsets/search', | ||
SearchVariantAnnotationSetsRequest, # noqa | ||
SearchVariantAnnotationSetsResponse), # noqa | ||
('/rnaquantificationsets/search', | ||
SearchRnaQuantificationSetsRequest, # noqa | ||
SearchRnaQuantificationSetsResponse), # noqa | ||
('/rnaquantifications/search', | ||
SearchRnaQuantificationsRequest, # noqa | ||
SearchRnaQuantificationsResponse), # noqa | ||
('/expressionlevels/search', | ||
SearchExpressionLevelsRequest, # noqa | ||
SearchExpressionLevelsResponse)] # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
""" | ||
Tests for protocol.py | ||
""" | ||
from __future__ import division | ||
from __future__ import print_function | ||
from __future__ import unicode_literals | ||
|
||
import datetime | ||
import unittest | ||
|
||
import google.protobuf.message as message | ||
import google.protobuf.struct_pb2 as struct_pb2 | ||
|
||
import ga4gh.schemas.protocol as protocol | ||
|
||
|
||
class TestProtocol(unittest.TestCase): | ||
|
||
# TODO this suite is not very sophisticated right now due to | ||
# using empty instead of instantiated protobuf classes to test various | ||
# aspects of the protocol module | ||
|
||
def testGetValueListName(self): | ||
for clazz in protocol.getProtocolClasses(): | ||
self.assertIsInstance(protocol.getValueListName(clazz), str) | ||
|
||
def testConvertDatetime(self): | ||
datetime_ = datetime.datetime.fromordinal(1234567) | ||
converted = protocol.convertDatetime(datetime_) | ||
self.assertEqual(converted, 44530905600000) | ||
|
||
def testGetValueFromValue(self): | ||
with self.assertRaises(TypeError): | ||
protocol.getValueFromValue(5) | ||
val = struct_pb2.Value() | ||
with self.assertRaises(AttributeError): | ||
protocol.getValueFromValue(val) | ||
# TODO add positive test | ||
|
||
def testToJsonAndFromJson(self): | ||
classes = protocol.getProtocolClasses() | ||
for clazz in classes: | ||
obj = clazz() | ||
jsonStr = protocol.toJson(obj) | ||
obj2 = protocol.fromJson(jsonStr, clazz) | ||
self.assertTrue(obj, obj2) | ||
|
||
def testToJsonDict(self): | ||
classes = protocol.getProtocolClasses() | ||
for clazz in classes: | ||
obj = clazz() | ||
jsonDict = protocol.toJsonDict(obj) | ||
self.assertIsInstance(jsonDict, dict) | ||
|
||
def testValidate(self): | ||
classes = protocol.getProtocolClasses() | ||
for clazz in classes: | ||
obj = clazz() | ||
jsonStr = protocol.toJson(obj) | ||
protocol.validate(jsonStr, clazz) | ||
|
||
def testGetProtocolClasses(self): | ||
classes = protocol.getProtocolClasses() | ||
self.assertGreater(len(classes), 0) | ||
for clazz in classes: | ||
self.assertTrue(issubclass(clazz, message.Message)) | ||
|
||
def testPostMethods(self): | ||
for postMethod in protocol.postMethods: | ||
self.assertEqual(len(postMethod), 3) | ||
self.assertIsInstance(postMethod[0], unicode) | ||
self.assertTrue(issubclass(postMethod[1], message.Message)) | ||
self.assertTrue(issubclass(postMethod[2], message.Message)) |