From 0e1c2d92eb39468bfcdb1146b98a14bbc7295482 Mon Sep 17 00:00:00 2001
From: Martijn Vermaat <martijn@vermaat.name>
Date: Sat, 27 Sep 2014 10:35:20 +0200
Subject: [PATCH] Use unicode string arguments in webservice interface
 definitions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This fixes Spyne to not crash on POST requests to the HTTP/RPC+JSON
webservice.

Note that all return values still use byte strings. Changing those will
touch a larger part of the codebase, and will be done in another commit.

As per [1]:

> Unlike the Python str, the Spyne String is not for arbitrary byte
> streams. You should not use it unless you are absolutely, positively
> sure that you need to deal with text data with an unknown encoding. In
> all other cases, you should just use the Unicode type. They actually
> look the same from outside, this distinction is made just to properly
> deal with the quirks surrounding Python-2’s unicode type.
>
> Remember that you have the ByteArray and File types at your disposal
> when you need to deal with arbitrary byte streams.
>
> The String type will be just an alias for Unicode once Spyne gets
> ported to Python 3. It might even be deprecated and removed in the
> future, so make sure you are using either Unicode or ByteArray in your
> interface definitions.

[1] http://spyne.io/docs/2.10/manual/03_types.html#strings
---
 mutalyzer/models.py       |  3 ++-
 mutalyzer/services/rpc.py | 50 +++++++++++++++++++--------------------
 requirements.txt          |  2 +-
 3 files changed, 28 insertions(+), 27 deletions(-)

diff --git a/mutalyzer/models.py b/mutalyzer/models.py
index f7356dda..387314ca 100644
--- a/mutalyzer/models.py
+++ b/mutalyzer/models.py
@@ -20,7 +20,7 @@ Additional attributes values for the spyne String model:
 """
 
 
-from spyne.model.primitive import String, Integer, Boolean, DateTime
+from spyne.model.primitive import String, Integer, Boolean, DateTime, Unicode
 from spyne.model.binary import ByteArray
 from spyne.model.complex import ComplexModel, Array
 
@@ -33,6 +33,7 @@ class Mandatory(object):
     the String model.
     """
     String = String(type_name='mandatory_string', min_occurs=1, nillable=False)
+    Unicode = Unicode(type_name='mandatory_unicode', min_occurs=1, nillable=False)
     Integer = Integer(type_name='mandatory_integer', min_occurs=1, nillable=False)
     Boolean = Boolean(type_name='mandatory_boolean', min_occurs=1, nillable=False)
     DateTime = DateTime(type_name='mandatory_date_time', min_occurs=1, nillable=False)
diff --git a/mutalyzer/services/rpc.py b/mutalyzer/services/rpc.py
index d681b94c..9ad68b94 100644
--- a/mutalyzer/services/rpc.py
+++ b/mutalyzer/services/rpc.py
@@ -11,7 +11,7 @@ Mutalyzer RPC services.
 
 from spyne.decorator import srpc
 from spyne.service import ServiceBase
-from spyne.model.primitive import String, Integer, Boolean, DateTime
+from spyne.model.primitive import String, Integer, Boolean, DateTime, Unicode
 from spyne.model.complex import Array
 from spyne.model.fault import Fault
 import os
@@ -51,7 +51,7 @@ class MutalyzerService(ServiceBase):
         super(MutalyzerService, self).__init__(environ)
     #__init__
 
-    @srpc(Mandatory.ByteArray, String, String,  _returns=String)
+    @srpc(Mandatory.ByteArray, Unicode, Unicode, _returns=String)
     def submitBatchJob(data, process='NameChecker', argument=''):
         """
         Submit a batch job.
@@ -115,7 +115,7 @@ class MutalyzerService(ServiceBase):
                                      batch_types[process], argument)
         return result_id
 
-    @srpc(Mandatory.String, _returns=Integer)
+    @srpc(Mandatory.Unicode, _returns=Integer)
     def monitorBatchJob(job_id):
         """
         Get the number of entries left for a batch job.
@@ -129,7 +129,7 @@ class MutalyzerService(ServiceBase):
         """
         return BatchQueueItem.query.join(BatchJob).filter_by(result_id=job_id).count()
 
-    @srpc(Mandatory.String, _returns=ByteArray)
+    @srpc(Mandatory.Unicode, _returns=ByteArray)
     def getBatchJob(job_id):
         """
         Get the result of a batch job.
@@ -155,7 +155,7 @@ class MutalyzerService(ServiceBase):
         handle = open(os.path.join(settings.CACHE_DIR, filename))
         return handle
 
-    @srpc(Mandatory.String, Mandatory.String, Mandatory.Integer, Boolean,
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, Mandatory.Integer, Boolean,
         _returns=Array(Mandatory.String))
     def getTranscripts(build, chrom, pos, versions=False) :
         """
@@ -215,7 +215,7 @@ class MutalyzerService(ServiceBase):
             return [m.accession for m in mappings]
     #getTranscripts
 
-    @srpc(Mandatory.String, Mandatory.String, _returns=Array(Mandatory.String))
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, _returns=Array(Mandatory.String))
     def getTranscriptsByGeneName(build, name):
         """
         Todo: documentation.
@@ -243,7 +243,7 @@ class MutalyzerService(ServiceBase):
         return ['%s.%s' % (m.accession, m.version) for m in mappings]
     #getTranscriptsByGene
 
-    @srpc(Mandatory.String, Mandatory.String, Mandatory.Integer,
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, Mandatory.Integer,
         Mandatory.Integer, Mandatory.Integer, _returns=Array(Mandatory.String))
     def getTranscriptsRange(build, chrom, pos1, pos2, method) :
         """
@@ -302,7 +302,7 @@ class MutalyzerService(ServiceBase):
         return [m.accession for m in mappings]
     #getTranscriptsRange
 
-    @srpc(Mandatory.String, Mandatory.String, Mandatory.Integer,
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, Mandatory.Integer,
         Mandatory.Integer, Mandatory.Integer,
         _returns=Array(TranscriptMappingInfo))
     def getTranscriptsMapping(build, chrom, pos1, pos2, method):
@@ -387,7 +387,7 @@ class MutalyzerService(ServiceBase):
         return transcripts
     #getTranscriptsMapping
 
-    @srpc(Mandatory.String, Mandatory.String, _returns=Mandatory.String)
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, _returns=Mandatory.String)
     def getGeneName(build, accno) :
         """
         Find the gene name associated with a transcript.
@@ -424,8 +424,8 @@ class MutalyzerService(ServiceBase):
         return mapping.gene
     #getGeneName
 
-    @srpc(Mandatory.String, Mandatory.String, Mandatory.String,
-        Mandatory.String, _returns=Mapping)
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, Mandatory.Unicode,
+        Mandatory.Unicode, _returns=Mapping)
     def mappingInfo(LOVD_ver, build, accNo, variant) :
         """
         Search for an NM number in the MySQL database, if the version
@@ -492,7 +492,7 @@ class MutalyzerService(ServiceBase):
         return result
     #mappingInfo
 
-    @srpc(Mandatory.String, Mandatory.String, Mandatory.String,
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, Mandatory.Unicode,
         _returns=Transcript)
     def transcriptInfo(LOVD_ver, build, accNo) :
         """
@@ -536,7 +536,7 @@ class MutalyzerService(ServiceBase):
         return T
     #transcriptInfo
 
-    @srpc(Mandatory.String, Mandatory.String, _returns=Mandatory.String)
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, _returns=Mandatory.String)
     def chromAccession(build, name) :
         """
         Get the accession number of a chromosome, given a name.
@@ -574,7 +574,7 @@ class MutalyzerService(ServiceBase):
         return chromosome.accession
     #chromAccession
 
-    @srpc(Mandatory.String, Mandatory.String, _returns=Mandatory.String)
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, _returns=Mandatory.String)
     def chromosomeName(build, accNo) :
         """
         Get the name of a chromosome, given a chromosome accession number.
@@ -612,7 +612,7 @@ class MutalyzerService(ServiceBase):
         return chromosome.name
     #chromosomeName
 
-    @srpc(Mandatory.String, Mandatory.String, _returns=Mandatory.String)
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, _returns=Mandatory.String)
     def getchromName(build, acc) :
         """
         Get the chromosome name, given a transcript identifier (NM number).
@@ -649,7 +649,7 @@ class MutalyzerService(ServiceBase):
         return mapping.chromosome.name
     #chromosomeName
 
-    @srpc(Mandatory.String, Mandatory.String, String,
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, Unicode,
           _returns=Array(Mandatory.String))
     def numberConversion(build, variant, gene=None):
         """
@@ -696,7 +696,7 @@ class MutalyzerService(ServiceBase):
         return result
     #numberConversion
 
-    @srpc(Mandatory.String, _returns=CheckSyntaxOutput)
+    @srpc(Mandatory.Unicode, _returns=CheckSyntaxOutput)
     def checkSyntax(variant):
         """
         Checks the syntax of a variant.
@@ -739,7 +739,7 @@ class MutalyzerService(ServiceBase):
         return result
     #checkSyntax
 
-    @srpc(Mandatory.String, _returns=MutalyzerOutput)
+    @srpc(Mandatory.Unicode, _returns=MutalyzerOutput)
     def runMutalyzer(variant) :
         """
         Run the Mutalyzer name checker.
@@ -860,7 +860,7 @@ class MutalyzerService(ServiceBase):
         return result
     #runMutalyzer
 
-    @srpc(Mandatory.String, Mandatory.String, _returns=TranscriptNameInfo)
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, _returns=TranscriptNameInfo)
     def getGeneAndTranscript(genomicReference, transcriptReference) :
         """
         Todo: documentation.
@@ -892,7 +892,7 @@ class MutalyzerService(ServiceBase):
         return ret
     #getGeneAndTranscript
 
-    @srpc(Mandatory.String, String, _returns=Array(TranscriptInfo))
+    @srpc(Mandatory.Unicode, Unicode, _returns=Array(TranscriptInfo))
     def getTranscriptsAndInfo(genomicReference, geneName=None):
         """
         Given a genomic reference, return all its transcripts with their
@@ -1081,7 +1081,7 @@ class MutalyzerService(ServiceBase):
         return ud
     #upLoadGenBankLocalFile
 
-    @srpc(Mandatory.String, _returns=Mandatory.String)
+    @srpc(Mandatory.Unicode, _returns=Mandatory.String)
     def uploadGenBankRemoteFile(url) :
         """
         Not implemented yet.
@@ -1089,7 +1089,7 @@ class MutalyzerService(ServiceBase):
         raise Fault('ENOTIMPLEMENTED', 'Not implemented yet')
     #upLoadGenBankRemoteFile
 
-    @srpc(Mandatory.String, Mandatory.String, Mandatory.Integer,
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, Mandatory.Integer,
         Mandatory.Integer, _returns=Mandatory.String)
     def sliceChromosomeByGene(geneSymbol, organism, upStream,
         downStream) :
@@ -1118,7 +1118,7 @@ class MutalyzerService(ServiceBase):
         return UD
     #sliceChromosomeByGene
 
-    @srpc(Mandatory.String, Mandatory.Integer, Mandatory.Integer,
+    @srpc(Mandatory.Unicode, Mandatory.Integer, Mandatory.Integer,
         Mandatory.Integer, _returns=Mandatory.String)
     def sliceChromosome(chromAccNo, start, end, orientation) :
         """
@@ -1201,7 +1201,7 @@ class MutalyzerService(ServiceBase):
         return 'pong'
     #ping
 
-    @srpc(Mandatory.String, Mandatory.String, _returns=Allele)
+    @srpc(Mandatory.Unicode, Mandatory.Unicode, _returns=Allele)
     def descriptionExtract(reference, observed):
         """
         Extract the HGVS variant description from a reference sequence and an
@@ -1253,7 +1253,7 @@ class MutalyzerService(ServiceBase):
         return map(cache_entry_to_soap, cache)
     #getCache
 
-    @srpc(Mandatory.String, _returns=Array(Mandatory.String))
+    @srpc(Mandatory.Unicode, _returns=Array(Mandatory.String))
     def getdbSNPDescriptions(rs_id):
         """
         Lookup HGVS descriptions for a dbSNP rs identifier.
diff --git a/requirements.txt b/requirements.txt
index ab361e7d..08f2d5de 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,7 +6,7 @@ pyparsing==2.0.1
 pytz==2013.9
 requests==2.2.1
 simplejson==3.3.3
--e git+https://github.com/LUMC/spyne.git@spyne-2.11.0-mutalyzer#egg=spyne
+spyne==2.11.0
 suds==0.4
 wsgiref==0.1.2
 xlrd==0.9.2
-- 
GitLab