Commit 2a4dc3c1 authored by Vermaat's avatar Vermaat
Browse files

Use unicode strings

Don't fix what ain't broken. Unfortunately, string handling in Mutalyzer
really is broken. So we fix it.

Internally, all strings should be represented by unicode strings as much as
possible. The main exception are large reference sequence strings. These can
often better be BioPython sequence objects, since that is how we usually get
them in the first place.

These changes will hopefully make Mutalyzer more reliable in working with
incoming data. As a bonus, they're a first (small) step towards Python 3
compatibility [1].

Our strategy is as follows:

1. We use `from __future__ import unicode_literals` at the top of every file.
2. All incoming strings are decoded to unicode (if necessary) as soon as
   possible.
3. Outgoing strings are encoded to UTF8 (if necessary) as late as possible.
4. BioPython sequence objects can be based on byte strings as well as unicode
   strings.
5. In the database, everything is UTF8.
6. We worry about uploaded and downloaded reference files and batch jobs in a
   later commit.

Point 1 will ensure that all string literals in our source code will be
unicode strings [2].

As for point 4, sometimes this may even change under our eyes (e.g., calling
`.reverse_complement()` will change it to a byte string). We don't care as
long as they're BioPython objects, only when we get the sequence out we must
have it as unicode string. Their contents are always in the ASCII range
anyway.

Although `Bio.Seq.reverse_complement` works fine on Python byte strings (and
we used to rely on that), it crashes on a Python unicode string. So we take
care to only use it on BioPython sequence objects and wrote our own reverse
complement function for unicode strings (`mutalyzer.util.reverse_complement`).

As for point 5, SQLAlchemy already does a very good job at presenting decoding
from and encoding to UTF8 for us.

The Spyne documentation has the following to say about their `String` and
`Unicode` types [3]:

> There are two string types in Spyne: `spyne.model.primitive.Unicode` and
> `spyne.model.primitive.String` whose native types are `unicode` and `str`
> respectively.
>
> 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.

So let's not ignore that and never use `String` anymore in our webservice
interface.

For the command line interface it's a bit more complicated, since there seems
to be no reliable way to get the encoding of command line arguments. We use
`sys.stdin.encoding` as a best guess.

For us to interpret a sequence of bytes as text, it's key to be aware of their
encoding. Once decoded, a text string can be safely used without having to
worry about bytes. Without unicode we're nothing, and nothing will help
us. Maybe we're lying, then you better not stay. But we could be safer, just
for one day. Oh-oh-oh-ohh, oh-oh-oh-ohh, just for one day.

[1] https://docs.python.org/2.7/howto/pyporting.html
[2] http://python-future.org/unicode_literals.html
[3] http://spyne.io/docs/2.10/manual/03_types.html#strings
parent 95d9f52e
......@@ -9,6 +9,8 @@ crashed.
"""
from __future__ import unicode_literals
import os
from mutalyzer import config
......
......@@ -15,6 +15,8 @@ Currently implemented checks:
"""
from __future__ import unicode_literals
import argparse
import logging
import sys
......
......@@ -17,6 +17,8 @@ to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -12,6 +12,8 @@ and printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -13,6 +13,8 @@ printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -14,6 +14,8 @@ service and printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -12,6 +12,8 @@ printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -13,6 +13,8 @@ web service and printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -13,6 +13,8 @@ printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -13,6 +13,8 @@ web service and printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -14,6 +14,8 @@ and printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -12,6 +12,8 @@ web service and printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -16,6 +16,8 @@ and printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -12,6 +12,8 @@ printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -10,6 +10,8 @@ printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -14,6 +14,8 @@ printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -13,6 +13,8 @@ web service and printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -13,6 +13,8 @@ printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -13,6 +13,8 @@ printed to standard output.
"""
from __future__ import unicode_literals
from mutalyzer.util import monkey_patch_suds; monkey_patch_suds()
import sys
......
......@@ -11,6 +11,8 @@
# This code is in the public domain; it can be used for whatever purpose
# with absolutely no restrictions.
from __future__ import unicode_literals
import sys
from SOAPpy import WSDL
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment