/lib/galaxy/model/custom_types.py

https://bitbucket.org/cistrome/cistrome-harvard/ · Python · 149 lines · 82 code · 26 blank · 41 comment · 19 complexity · 21839754e7ee38749e2de04a16a63c4c MD5 · raw file

  1. from sqlalchemy.types import *
  2. import json
  3. import pickle
  4. import copy
  5. import uuid
  6. import binascii
  7. from galaxy.util.bunch import Bunch
  8. from galaxy.util.aliaspickler import AliasPickleModule
  9. # For monkeypatching BIGINT
  10. import sqlalchemy.dialects.sqlite
  11. import sqlalchemy.dialects.postgresql
  12. import sqlalchemy.dialects.mysql
  13. import logging
  14. log = logging.getLogger( __name__ )
  15. # Default JSON encoder and decoder
  16. json_encoder = json.JSONEncoder( sort_keys=True )
  17. json_decoder = json.JSONDecoder( )
  18. def _sniffnfix_pg9_hex(value):
  19. """
  20. Sniff for and fix postgres 9 hex decoding issue
  21. """
  22. try:
  23. if value[0] == 'x':
  24. return binascii.unhexlify(value[1:])
  25. elif value.startswith( '\\x' ):
  26. return binascii.unhexlify( value[2:] )
  27. else:
  28. return value
  29. except Exception, ex:
  30. return value
  31. class JSONType( TypeDecorator ):
  32. """
  33. Defines a JSONType for SQLAlchemy. Takes a primitive as input and
  34. JSONifies it. This should replace PickleType throughout Galaxy.
  35. """
  36. impl = LargeBinary
  37. def process_bind_param( self, value, dialect ):
  38. if value is None:
  39. return None
  40. return json_encoder.encode( value )
  41. def process_result_value( self, value, dialect ):
  42. if value is None:
  43. return None
  44. return json_decoder.decode( str( _sniffnfix_pg9_hex(value) ) )
  45. def copy_value( self, value ):
  46. # return json_decoder.decode( json_encoder.encode( value ) )
  47. return copy.deepcopy( value )
  48. def compare_values( self, x, y ):
  49. # return json_encoder.encode( x ) == json_encoder.encode( y )
  50. return ( x == y )
  51. def is_mutable( self ):
  52. return True
  53. metadata_pickler = AliasPickleModule( {
  54. ( "cookbook.patterns", "Bunch" ) : ( "galaxy.util.bunch" , "Bunch" )
  55. } )
  56. class MetadataType( JSONType ):
  57. """
  58. Backward compatible metadata type. Can read pickles or JSON, but always
  59. writes in JSON.
  60. """
  61. def process_result_value( self, value, dialect ):
  62. if value is None:
  63. return None
  64. ret = None
  65. try:
  66. ret = metadata_pickler.loads( str( value ) )
  67. if ret:
  68. ret = dict( ret.__dict__ )
  69. except:
  70. try:
  71. ret = json_decoder.decode( str( _sniffnfix_pg9_hex(value) ) )
  72. except:
  73. ret = None
  74. return ret
  75. class UUIDType(TypeDecorator):
  76. """
  77. Platform-independent UUID type.
  78. Based on http://docs.sqlalchemy.org/en/rel_0_8/core/types.html#backend-agnostic-guid-type
  79. Changed to remove sqlalchemy 0.8 specific code
  80. CHAR(32), storing as stringified hex values.
  81. """
  82. impl = CHAR
  83. def load_dialect_impl(self, dialect):
  84. return dialect.type_descriptor(CHAR(32))
  85. def process_bind_param(self, value, dialect):
  86. if value is None:
  87. return value
  88. else:
  89. if not isinstance(value, uuid.UUID):
  90. return "%.32x" % uuid.UUID(value)
  91. else:
  92. # hexstring
  93. return "%.32x" % value
  94. def process_result_value(self, value, dialect):
  95. if value is None:
  96. return value
  97. else:
  98. return uuid.UUID(value)
  99. class TrimmedString( TypeDecorator ):
  100. impl = String
  101. def process_bind_param( self, value, dialect ):
  102. """Automatically truncate string values"""
  103. if self.impl.length and value is not None:
  104. value = value[0:self.impl.length]
  105. return value
  106. #class BigInteger( Integer ):
  107. #"""
  108. #A type for bigger ``int`` integers.
  109. #Typically generates a ``BIGINT`` in DDL, and otherwise acts like
  110. #a normal :class:`Integer` on the Python side.
  111. #"""
  112. #class BIGINT( BigInteger ):
  113. #"""The SQL BIGINT type."""
  114. #class SLBigInteger( BigInteger ):
  115. #def get_col_spec( self ):
  116. #return "BIGINT"
  117. #sqlalchemy.dialects.sqlite.SLBigInteger = SLBigInteger
  118. #sqlalchemy.dialects.sqlite.colspecs[BigInteger] = SLBigInteger
  119. #sqlalchemy.dialects.sqlite.ischema_names['BIGINT'] = SLBigInteger
  120. #sqlalchemy.dialects.postgres.colspecs[BigInteger] = sqlalchemy.dialects.postgres.PGBigInteger
  121. #sqlalchemy.dialects.mysql.colspecs[BigInteger] = sqlalchemy.dialects.mysql.MSBigInteger