This is inspired by work done by Thiago Bellini. I've taken a different approach in a few areas which are useful to discuss briefly here (and some of this should end up in release notes):
* I generally wrote "bytes" rather than "six.binary_type", since it's shorter and works in all supported versions of Python.
* In the SQLite backend, there's no need to use memoryview, because the sqlite3 module in Python 3 automatically converts between the SQLite BLOB type and bytes.
* Some exception messages have changed slightly for clarity.
* On Python 3, raw=True and token=True in storm.expr.Compile.__call__ only treats str specially, not bytes and str, because ultimately the compiler is assembling a text string to send to the database.
* On Python 3, storm.tracer.BaseStatementTracer.connection_raw_execute renders text parameters using ascii() rather than by encoding to bytes and then calling repr(). While this does result in slightly different output from Python 2, it's normally more useful since the encoding is in terms of Unicode codepoints rather than UTF-8.
* storm.sqlobject.AutoUnicodeVariable (and hence StringCol) explicitly documents that it only accepts text on Python 3, since native strings are already Unicode there so there's much less need for the porting affordance.