hydrus/hydrus/core/HydrusSerialisable.py

771 lines
28 KiB
Python
Raw Normal View History

2021-06-02 21:59:19 +00:00
import hashlib
2015-04-22 22:57:25 +00:00
import json
2021-01-20 22:22:03 +00:00
import os
2020-07-29 20:52:44 +00:00
2021-12-22 22:31:23 +00:00
from hydrus.core import HydrusCompression
from hydrus.core import HydrusConstants as HC
2020-07-29 20:52:44 +00:00
from hydrus.core import HydrusData
2021-01-20 22:22:03 +00:00
from hydrus.core import HydrusExceptions
2020-07-29 20:52:44 +00:00
2022-12-14 22:22:11 +00:00
META_SERIALISABLE_TYPE_JSON_OK = 0
META_SERIALISABLE_TYPE_JSON_BYTES = 1
META_SERIALISABLE_TYPE_HYDRUS_SERIALISABLE = 2
2015-04-22 22:57:25 +00:00
SERIALISABLE_TYPE_BASE = 0
SERIALISABLE_TYPE_BASE_NAMED = 1
2019-06-05 19:42:39 +00:00
SERIALISABLE_TYPE_SHORTCUT_SET = 2
2020-06-11 12:01:08 +00:00
SERIALISABLE_TYPE_SUBSCRIPTION_LEGACY = 3
2015-05-13 20:22:39 +00:00
SERIALISABLE_TYPE_PERIODIC = 4
2015-10-07 21:56:22 +00:00
SERIALISABLE_TYPE_GALLERY_IDENTIFIER = 5
2017-09-20 19:47:31 +00:00
SERIALISABLE_TYPE_TAG_IMPORT_OPTIONS = 6
SERIALISABLE_TYPE_FILE_IMPORT_OPTIONS = 7
2018-06-27 19:27:05 +00:00
SERIALISABLE_TYPE_FILE_SEED_CACHE = 8
2015-05-20 21:31:40 +00:00
SERIALISABLE_TYPE_HDD_IMPORT = 9
2015-06-03 21:05:13 +00:00
SERIALISABLE_TYPE_SERVER_TO_CLIENT_CONTENT_UPDATE_PACKAGE = 10
SERIALISABLE_TYPE_SERVER_TO_CLIENT_SERVICE_UPDATE_PACKAGE = 11
2015-06-17 20:01:41 +00:00
SERIALISABLE_TYPE_MANAGEMENT_CONTROLLER = 12
2021-06-02 21:59:19 +00:00
SERIALISABLE_TYPE_GUI_SESSION_LEGACY = 13
2015-06-24 22:10:14 +00:00
SERIALISABLE_TYPE_PREDICATE = 14
SERIALISABLE_TYPE_FILE_SEARCH_CONTEXT = 15
SERIALISABLE_TYPE_EXPORT_FOLDER = 16
2018-05-23 21:05:06 +00:00
SERIALISABLE_TYPE_WATCHER_IMPORT = 17
2018-04-05 01:22:26 +00:00
SERIALISABLE_TYPE_SIMPLE_DOWNLOADER_IMPORT = 18
2015-09-09 22:04:39 +00:00
SERIALISABLE_TYPE_IMPORT_FOLDER = 19
2018-07-18 21:07:15 +00:00
SERIALISABLE_TYPE_MULTIPLE_GALLERY_IMPORT = 20
2015-10-07 21:56:22 +00:00
SERIALISABLE_TYPE_DICTIONARY = 21
SERIALISABLE_TYPE_CLIENT_OPTIONS = 22
2015-10-14 21:02:25 +00:00
SERIALISABLE_TYPE_CONTENT = 23
2017-03-02 02:14:56 +00:00
SERIALISABLE_TYPE_PETITION = 24
2015-10-14 21:02:25 +00:00
SERIALISABLE_TYPE_ACCOUNT_IDENTIFIER = 25
SERIALISABLE_TYPE_LIST = 26
2016-09-21 19:54:04 +00:00
SERIALISABLE_TYPE_PARSE_FORMULA_HTML = 27
2016-09-07 20:01:05 +00:00
SERIALISABLE_TYPE_URLS_IMPORT = 28
2016-10-05 20:22:40 +00:00
SERIALISABLE_TYPE_PARSE_NODE_CONTENT_LINK = 29
2017-12-13 22:33:07 +00:00
SERIALISABLE_TYPE_CONTENT_PARSER = 30
2016-09-21 19:54:04 +00:00
SERIALISABLE_TYPE_PARSE_FORMULA_JSON = 31
2016-10-05 20:22:40 +00:00
SERIALISABLE_TYPE_PARSE_ROOT_FILE_LOOKUP = 32
2017-03-02 02:14:56 +00:00
SERIALISABLE_TYPE_BYTES_DICT = 33
SERIALISABLE_TYPE_CONTENT_UPDATE = 34
SERIALISABLE_TYPE_CREDENTIALS = 35
SERIALISABLE_TYPE_DEFINITIONS_UPDATE = 36
SERIALISABLE_TYPE_METADATA = 37
SERIALISABLE_TYPE_BANDWIDTH_RULES = 38
SERIALISABLE_TYPE_BANDWIDTH_TRACKER = 39
SERIALISABLE_TYPE_CLIENT_TO_SERVER_UPDATE = 40
2017-04-12 21:46:46 +00:00
SERIALISABLE_TYPE_SHORTCUT = 41
SERIALISABLE_TYPE_APPLICATION_COMMAND = 42
2022-12-14 22:22:11 +00:00
SERIALISABLE_TYPE_DUPLICATE_CONTENT_MERGE_OPTIONS = 43
2018-04-25 22:07:52 +00:00
SERIALISABLE_TYPE_TAG_FILTER = 44
2021-01-13 21:48:58 +00:00
SERIALISABLE_TYPE_NETWORK_BANDWIDTH_MANAGER_LEGACY = 45
SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER_LEGACY = 46
2017-06-28 20:23:21 +00:00
SERIALISABLE_TYPE_NETWORK_CONTEXT = 47
SERIALISABLE_TYPE_NETWORK_LOGIN_MANAGER = 48
2017-08-09 21:33:51 +00:00
SERIALISABLE_TYPE_MEDIA_SORT = 49
2019-05-08 21:06:42 +00:00
SERIALISABLE_TYPE_URL_CLASS = 50
2017-09-27 21:52:54 +00:00
SERIALISABLE_TYPE_STRING_MATCH = 51
2017-11-08 22:07:12 +00:00
SERIALISABLE_TYPE_CHECKER_OPTIONS = 52
2017-10-04 17:51:58 +00:00
SERIALISABLE_TYPE_NETWORK_DOMAIN_MANAGER = 53
2020-06-11 12:01:08 +00:00
SERIALISABLE_TYPE_SUBSCRIPTION_QUERY_LEGACY = 54
2017-11-15 22:35:49 +00:00
SERIALISABLE_TYPE_STRING_CONVERTER = 55
SERIALISABLE_TYPE_FILENAME_TAGGING_OPTIONS = 56
2018-06-27 19:27:05 +00:00
SERIALISABLE_TYPE_FILE_SEED = 57
2017-12-13 22:33:07 +00:00
SERIALISABLE_TYPE_PAGE_PARSER = 58
2018-01-31 22:58:15 +00:00
SERIALISABLE_TYPE_PARSE_FORMULA_COMPOUND = 59
2018-02-07 23:40:33 +00:00
SERIALISABLE_TYPE_PARSE_FORMULA_CONTEXT_VARIABLE = 60
2018-02-21 21:59:37 +00:00
SERIALISABLE_TYPE_TAG_SUMMARY_GENERATOR = 61
2018-04-11 22:30:40 +00:00
SERIALISABLE_TYPE_PARSE_RULE_HTML = 62
SERIALISABLE_TYPE_SIMPLE_DOWNLOADER_PARSE_FORMULA = 63
2018-05-16 20:09:50 +00:00
SERIALISABLE_TYPE_MULTIPLE_WATCHER_IMPORT = 64
2018-06-27 19:27:05 +00:00
SERIALISABLE_TYPE_SERVICE_TAG_IMPORT_OPTIONS = 65
SERIALISABLE_TYPE_GALLERY_SEED = 66
SERIALISABLE_TYPE_GALLERY_SEED_LOG = 67
2018-07-18 21:07:15 +00:00
SERIALISABLE_TYPE_GALLERY_IMPORT = 68
2018-08-22 21:10:59 +00:00
SERIALISABLE_TYPE_GALLERY_URL_GENERATOR = 69
SERIALISABLE_TYPE_NESTED_GALLERY_URL_GENERATOR = 70
2018-10-03 21:00:15 +00:00
SERIALISABLE_TYPE_DOMAIN_METADATA_PACKAGE = 71
2018-10-17 21:00:09 +00:00
SERIALISABLE_TYPE_LOGIN_CREDENTIAL_DEFINITION = 72
SERIALISABLE_TYPE_LOGIN_SCRIPT_DOMAIN = 73
SERIALISABLE_TYPE_LOGIN_STEP = 74
2019-01-23 22:19:16 +00:00
SERIALISABLE_TYPE_CLIENT_API_MANAGER = 75
SERIALISABLE_TYPE_CLIENT_API_PERMISSIONS = 76
2019-02-27 23:03:30 +00:00
SERIALISABLE_TYPE_SERVICE_KEYS_TO_TAGS = 77
2019-08-21 21:34:01 +00:00
SERIALISABLE_TYPE_MEDIA_COLLECT = 78
2019-10-02 23:38:59 +00:00
SERIALISABLE_TYPE_TAG_DISPLAY_MANAGER = 79
2022-07-20 19:17:03 +00:00
SERIALISABLE_TYPE_tag_context = 80
2020-03-11 21:52:11 +00:00
SERIALISABLE_TYPE_FAVOURITE_SEARCH_MANAGER = 81
2020-05-06 21:31:41 +00:00
SERIALISABLE_TYPE_NOTE_IMPORT_OPTIONS = 82
2020-05-13 19:03:16 +00:00
SERIALISABLE_TYPE_STRING_SPLITTER = 83
SERIALISABLE_TYPE_STRING_PROCESSOR = 84
2020-05-20 21:36:02 +00:00
SERIALISABLE_TYPE_TAG_AUTOCOMPLETE_OPTIONS = 85
2020-06-11 12:01:08 +00:00
SERIALISABLE_TYPE_SUBSCRIPTION_QUERY_LOG_CONTAINER = 86
SERIALISABLE_TYPE_SUBSCRIPTION_QUERY_HEADER = 87
SERIALISABLE_TYPE_SUBSCRIPTION = 88
SERIALISABLE_TYPE_FILE_SEED_CACHE_STATUS = 89
2020-06-17 21:31:54 +00:00
SERIALISABLE_TYPE_SUBSCRIPTION_CONTAINER = 90
2020-07-15 20:52:09 +00:00
SERIALISABLE_TYPE_COLUMN_LIST_STATUS = 91
SERIALISABLE_TYPE_COLUMN_LIST_MANAGER = 92
2020-07-22 20:59:16 +00:00
SERIALISABLE_TYPE_NUMBER_TEST = 93
2021-01-13 21:48:58 +00:00
SERIALISABLE_TYPE_NETWORK_BANDWIDTH_MANAGER = 94
SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER = 95
SERIALISABLE_TYPE_NETWORK_SESSION_MANAGER_SESSION_CONTAINER = 96
SERIALISABLE_TYPE_NETWORK_BANDWIDTH_MANAGER_TRACKER_CONTAINER = 97
2021-02-17 18:22:44 +00:00
SERIALISABLE_TYPE_SIDECAR_EXPORTER = 98
2021-03-03 22:23:35 +00:00
SERIALISABLE_TYPE_STRING_SORTER = 99
2021-03-10 23:10:11 +00:00
SERIALISABLE_TYPE_STRING_SLICER = 100
SERIALISABLE_TYPE_TAG_SORT = 101
2021-04-07 21:26:45 +00:00
SERIALISABLE_TYPE_ACCOUNT_TYPE = 102
2022-05-25 21:30:53 +00:00
SERIALISABLE_TYPE_LOCATION_CONTEXT = 103
2021-06-02 21:59:19 +00:00
SERIALISABLE_TYPE_GUI_SESSION_CONTAINER = 104
SERIALISABLE_TYPE_GUI_SESSION_PAGE_DATA = 105
SERIALISABLE_TYPE_GUI_SESSION_CONTAINER_PAGE_NOTEBOOK = 106
SERIALISABLE_TYPE_GUI_SESSION_CONTAINER_PAGE_SINGLE = 107
2021-11-24 21:59:58 +00:00
SERIALISABLE_TYPE_PRESENTATION_IMPORT_OPTIONS = 108
2022-07-13 21:35:17 +00:00
SERIALISABLE_TYPE_METADATA_SINGLE_FILE_ROUTER = 109
2022-10-26 20:43:00 +00:00
SERIALISABLE_TYPE_METADATA_SINGLE_FILE_IMPORTER_TXT = 110
SERIALISABLE_TYPE_METADATA_SINGLE_FILE_IMPORTER_MEDIA_TAGS = 111
2022-07-13 21:35:17 +00:00
SERIALISABLE_TYPE_STRING_TAG_FILTER = 112
2022-10-26 20:43:00 +00:00
SERIALISABLE_TYPE_METADATA_SINGLE_FILE_EXPORTER_JSON = 113
SERIALISABLE_TYPE_METADATA_SINGLE_FILE_IMPORTER_JSON = 114
SERIALISABLE_TYPE_METADATA_SINGLE_FILE_EXPORTER_MEDIA_TAGS = 115
SERIALISABLE_TYPE_METADATA_SINGLE_FILE_EXPORTER_TXT = 116
SERIALISABLE_TYPE_METADATA_SINGLE_FILE_EXPORTER_MEDIA_URLS = 117
SERIALISABLE_TYPE_METADATA_SINGLE_FILE_IMPORTER_MEDIA_URLS = 118
2015-04-22 22:57:25 +00:00
SERIALISABLE_TYPES_TO_OBJECT_TYPES = {}
2021-12-22 22:31:23 +00:00
def CreateFromNetworkBytes( network_bytes: bytes, raise_error_on_future_version = False ):
2015-05-13 20:22:39 +00:00
2021-12-22 22:31:23 +00:00
obj_string = HydrusCompression.DecompressBytesToString( network_bytes )
2019-01-09 22:59:03 +00:00
2021-01-20 22:22:03 +00:00
return CreateFromString( obj_string, raise_error_on_future_version = raise_error_on_future_version )
2015-06-03 21:05:13 +00:00
2021-01-20 22:22:03 +00:00
def CreateFromNoneableSerialisableTuple( obj_tuple_or_none, raise_error_on_future_version = False ):
2020-06-11 12:01:08 +00:00
if obj_tuple_or_none is None:
return None
else:
2021-01-20 22:22:03 +00:00
return CreateFromSerialisableTuple( obj_tuple_or_none, raise_error_on_future_version = raise_error_on_future_version )
2020-06-11 12:01:08 +00:00
2021-01-20 22:22:03 +00:00
def CreateFromString( obj_string, raise_error_on_future_version = False ):
2015-06-03 21:05:13 +00:00
obj_tuple = json.loads( obj_string )
2021-01-20 22:22:03 +00:00
return CreateFromSerialisableTuple( obj_tuple, raise_error_on_future_version = raise_error_on_future_version )
2015-06-03 21:05:13 +00:00
2021-01-20 22:22:03 +00:00
def CreateFromSerialisableTuple( obj_tuple, raise_error_on_future_version = False ):
2015-06-03 21:05:13 +00:00
if len( obj_tuple ) == 3:
2015-06-24 22:10:14 +00:00
( serialisable_type, version, serialisable_info ) = obj_tuple
2015-06-03 21:05:13 +00:00
obj = SERIALISABLE_TYPES_TO_OBJECT_TYPES[ serialisable_type ]()
else:
2015-06-24 22:10:14 +00:00
( serialisable_type, name, version, serialisable_info ) = obj_tuple
2015-06-03 21:05:13 +00:00
obj = SERIALISABLE_TYPES_TO_OBJECT_TYPES[ serialisable_type ]( name )
2015-05-13 20:22:39 +00:00
2021-01-20 22:22:03 +00:00
obj.InitialiseFromSerialisableInfo( version, serialisable_info, raise_error_on_future_version = raise_error_on_future_version )
2015-05-13 20:22:39 +00:00
return obj
2015-06-03 21:05:13 +00:00
2020-06-11 12:01:08 +00:00
def GetNoneableSerialisableTuple( obj_or_none ):
if obj_or_none is None:
return None
else:
return obj_or_none.GetSerialisableTuple()
2018-04-11 22:30:40 +00:00
def SetNonDupeName( obj, disallowed_names ):
2020-03-11 21:52:11 +00:00
non_dupe_name = HydrusData.GetNonDupeName( obj.GetName(), disallowed_names )
2018-04-11 22:30:40 +00:00
obj.SetName( non_dupe_name )
2021-01-20 22:22:03 +00:00
def ObjectVersionIsFromTheFuture( obj_tuple ):
if len( obj_tuple ) == 3:
( serialisable_type, version, serialisable_info ) = obj_tuple
else:
( serialisable_type, name, version, serialisable_info ) = obj_tuple
return SERIALISABLE_TYPES_TO_OBJECT_TYPES[ serialisable_type ].SERIALISABLE_VERSION > version
2015-04-22 22:57:25 +00:00
class SerialisableBase( object ):
SERIALISABLE_TYPE = SERIALISABLE_TYPE_BASE
2017-11-29 21:48:23 +00:00
SERIALISABLE_NAME = 'Base Serialisable Object'
2015-06-03 21:05:13 +00:00
SERIALISABLE_VERSION = 1
2015-04-22 22:57:25 +00:00
2023-01-04 22:22:08 +00:00
# don't make an __eq__ here without more testing and research, it messes a bunch of things up in sets and hashing and stuff
2015-04-22 22:57:25 +00:00
def _GetSerialisableInfo( self ):
raise NotImplementedError()
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
raise NotImplementedError()
2015-07-22 19:40:39 +00:00
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
2015-04-22 22:57:25 +00:00
2015-07-22 19:40:39 +00:00
return old_serialisable_info
2015-04-22 22:57:25 +00:00
2016-07-20 19:57:10 +00:00
2019-01-09 22:59:03 +00:00
def DumpToNetworkBytes( self ):
2015-10-14 21:02:25 +00:00
obj_string = self.DumpToString()
2021-12-22 22:31:23 +00:00
return HydrusCompression.CompressStringToBytes( obj_string )
2015-10-14 21:02:25 +00:00
2015-04-22 22:57:25 +00:00
2015-10-14 21:02:25 +00:00
def DumpToString( self ):
2015-04-22 22:57:25 +00:00
2015-10-14 21:02:25 +00:00
obj_tuple = self.GetSerialisableTuple()
2015-04-22 22:57:25 +00:00
2015-10-14 21:02:25 +00:00
return json.dumps( obj_tuple )
2015-04-22 22:57:25 +00:00
2016-10-05 20:22:40 +00:00
def Duplicate( self ):
return CreateFromString( self.DumpToString() )
2021-06-02 21:59:19 +00:00
def GetSerialisedHash( self ):
# as a note, this should not be relied on in future--the serialised string could change due to object updates, or in rare cases, because the contained objects are still hot
return hashlib.sha256( bytes( self.DumpToString(), 'utf-8' ) ).digest()
2015-10-14 21:02:25 +00:00
def GetSerialisableTuple( self ):
2015-04-22 22:57:25 +00:00
2019-02-06 22:41:35 +00:00
if hasattr( self, '_lock' ):
with getattr( self, '_lock' ):
serialisable_info = self._GetSerialisableInfo()
else:
serialisable_info = self._GetSerialisableInfo()
return ( self.SERIALISABLE_TYPE, self.SERIALISABLE_VERSION, serialisable_info )
2015-05-13 20:22:39 +00:00
2022-12-21 22:00:27 +00:00
def InitialiseFromSerialisableInfo( self, original_version, serialisable_info, raise_error_on_future_version = False ):
2021-01-20 22:22:03 +00:00
2022-12-21 22:00:27 +00:00
object_is_newer = original_version > self.SERIALISABLE_VERSION
if object_is_newer:
2021-01-20 22:22:03 +00:00
if raise_error_on_future_version:
2022-12-21 22:00:27 +00:00
message = 'Unfortunately, an object of type {} could not be loaded because it was created in a client/server that uses an updated version of that object! We support up to version {}, but the object was version {}.'.format( self.SERIALISABLE_NAME, self.SERIALISABLE_VERSION, original_version )
2021-01-20 22:22:03 +00:00
message += os.linesep * 2
2022-12-21 22:00:27 +00:00
message += 'Please update your client/server to import this object.'
2021-01-20 22:22:03 +00:00
raise HydrusExceptions.SerialisationException( message )
else:
2022-12-21 22:00:27 +00:00
message = 'An object of type {} was created in a client/server that uses an updated version of that object! We support versions up to {}, but the object was version {}. For now, we will try to continue work, but things may break. If you know why this has occured, please correct it. If you do not, please let hydrus dev know.'.format( self.SERIALISABLE_NAME, self.SERIALISABLE_VERSION, original_version )
2021-01-20 22:22:03 +00:00
HydrusData.ShowText( message )
2015-04-22 22:57:25 +00:00
2022-12-21 22:00:27 +00:00
try:
current_version = original_version
while current_version < self.SERIALISABLE_VERSION:
( current_version, serialisable_info ) = self._UpdateSerialisableInfo( current_version, serialisable_info )
except:
2015-04-22 22:57:25 +00:00
2022-12-21 22:00:27 +00:00
raise HydrusExceptions.SerialisationException( 'Could not update this object of type {} from version {} to {}!'.format( self.SERIALISABLE_NAME, original_version, self.SERIALISABLE_VERSION ) )
2015-04-22 22:57:25 +00:00
2022-12-21 22:00:27 +00:00
try:
self._InitialiseFromSerialisableInfo( serialisable_info )
except:
if object_is_newer:
raise HydrusExceptions.SerialisationException( 'An object of type {} was created in a client/server that uses an updated version of that object! We support versions up to {}, but the object was version {}. I tried to load it, but the initialisation failed. You probably need to update your client/server.'.format( self.SERIALISABLE_NAME, self.SERIALISABLE_VERSION, original_version ) )
else:
raise HydrusExceptions.SerialisationException( 'Could not initialise this object of type {}!'.format( self.SERIALISABLE_NAME ) )
2015-04-22 22:57:25 +00:00
2022-12-14 22:22:11 +00:00
def ConvertObjectToMetaSerialisableTuple( obj ):
if isinstance( obj, SerialisableBase ):
metatype = META_SERIALISABLE_TYPE_HYDRUS_SERIALISABLE
serialisable = obj.GetSerialisableTuple()
elif isinstance( obj, bytes ):
metatype = META_SERIALISABLE_TYPE_JSON_BYTES
serialisable = obj.hex()
else:
metatype = META_SERIALISABLE_TYPE_JSON_OK
serialisable = obj
return ( metatype, serialisable )
def ConvertMetaSerialisableTupleToObject( meta_tuple ):
( metatype, serialisable ) = meta_tuple
if metatype == META_SERIALISABLE_TYPE_HYDRUS_SERIALISABLE:
obj = CreateFromSerialisableTuple( serialisable )
elif metatype == META_SERIALISABLE_TYPE_JSON_BYTES:
obj = bytes.fromhex( serialisable )
else:
obj = serialisable
return obj
2015-04-22 22:57:25 +00:00
class SerialisableBaseNamed( SerialisableBase ):
SERIALISABLE_TYPE = SERIALISABLE_TYPE_BASE_NAMED
2017-11-29 21:48:23 +00:00
SERIALISABLE_NAME = 'Named Base Serialisable Object'
2015-04-22 22:57:25 +00:00
def __init__( self, name ):
SerialisableBase.__init__( self )
self._name = name
def _GetSerialisableInfo( self ):
raise NotImplementedError()
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
raise NotImplementedError()
2015-10-14 21:02:25 +00:00
def GetSerialisableTuple( self ):
return ( self.SERIALISABLE_TYPE, self._name, self.SERIALISABLE_VERSION, self._GetSerialisableInfo() )
2016-07-20 19:57:10 +00:00
2015-04-22 22:57:25 +00:00
def GetName( self ): return self._name
def SetName( self, name ): self._name = name
2015-10-07 21:56:22 +00:00
2017-08-09 21:33:51 +00:00
def SetNonDupeName( self, disallowed_names ):
2020-03-11 21:52:11 +00:00
self._name = HydrusData.GetNonDupeName( self._name, disallowed_names )
2017-08-09 21:33:51 +00:00
2015-10-07 21:56:22 +00:00
class SerialisableDictionary( SerialisableBase, dict ):
SERIALISABLE_TYPE = SERIALISABLE_TYPE_DICTIONARY
2017-11-29 21:48:23 +00:00
SERIALISABLE_NAME = 'Serialisable Dictionary'
SERIALISABLE_VERSION = 2 # this is used in the network, do not update it casually!
2015-10-07 21:56:22 +00:00
def __init__( self, *args, **kwargs ):
dict.__init__( self, *args, **kwargs )
SerialisableBase.__init__( self )
def _GetSerialisableInfoVersion1( self ):
simple_key_simple_value_pairs = []
simple_key_serialisable_value_pairs = []
serialisable_key_simple_value_pairs = []
serialisable_key_serialisable_value_pairs = []
for ( key, value ) in self.items():
if isinstance( key, SerialisableBase ):
serialisable_key = key.GetSerialisableTuple()
if isinstance( value, SerialisableBase ):
serialisable_value = value.GetSerialisableTuple()
serialisable_key_serialisable_value_pairs.append( ( serialisable_key, serialisable_value ) )
else:
serialisable_value = value
serialisable_key_simple_value_pairs.append( ( serialisable_key, serialisable_value ) )
else:
serialisable_key = key
if isinstance( value, SerialisableBase ):
serialisable_value = value.GetSerialisableTuple()
simple_key_serialisable_value_pairs.append( ( serialisable_key, serialisable_value ) )
else:
serialisable_value = value
simple_key_simple_value_pairs.append( ( serialisable_key, serialisable_value ) )
return ( simple_key_simple_value_pairs, simple_key_serialisable_value_pairs, serialisable_key_simple_value_pairs, serialisable_key_serialisable_value_pairs )
2015-10-07 21:56:22 +00:00
def _GetSerialisableInfo( self ):
2022-12-14 22:22:11 +00:00
meta_keys_and_meta_values = []
2015-10-07 21:56:22 +00:00
2020-07-15 20:52:09 +00:00
for ( key, value ) in self.items():
2015-10-07 21:56:22 +00:00
2022-12-07 22:41:53 +00:00
# after being caught out on a recursive legacy thing here, we now coerce. I'm 99.7% confident it can't hurt
if isinstance( value, list ) and not isinstance( value, SerialisableBase ):
value = SerialisableList( value )
elif isinstance( value, dict ) and not isinstance( value, SerialisableBase ):
value = SerialisableDictionary( value )
2022-12-14 22:22:11 +00:00
meta_key = ConvertObjectToMetaSerialisableTuple( key )
meta_value = ConvertObjectToMetaSerialisableTuple( value )
meta_keys_and_meta_values.append( ( meta_key, meta_value ) )
2015-10-07 21:56:22 +00:00
2022-12-14 22:22:11 +00:00
return meta_keys_and_meta_values
2015-10-07 21:56:22 +00:00
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
2021-01-20 22:22:03 +00:00
have_shown_load_error = False
2022-12-14 22:22:11 +00:00
meta_keys_and_meta_values = serialisable_info
2015-10-07 21:56:22 +00:00
2022-12-14 22:22:11 +00:00
for ( meta_key, meta_value ) in meta_keys_and_meta_values:
2015-10-07 21:56:22 +00:00
2021-01-20 22:22:03 +00:00
try:
2022-12-14 22:22:11 +00:00
key = ConvertMetaSerialisableTupleToObject( meta_key )
value = ConvertMetaSerialisableTupleToObject( meta_value )
2021-01-20 22:22:03 +00:00
except HydrusExceptions.SerialisationException as e:
if not have_shown_load_error:
2022-12-14 22:22:11 +00:00
HydrusData.ShowText( 'An object in a dictionary could not load. It has been discarded from the dictionary. More may also have failed to load, but to stop error spam, they will go silently. Your client may be running on code versions behind its database. Depending on the severity of this error, you may need to rollback to a previous backup. If you have no backup, you may want to kill your hydrus process now to stop the cleansed dictionary ever being saved back to the db.' )
2021-01-20 22:22:03 +00:00
HydrusData.ShowException( e )
have_shown_load_error = True
continue
2015-10-07 21:56:22 +00:00
self[ key ] = value
2022-12-14 22:22:11 +00:00
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
if version == 1:
2015-10-07 21:56:22 +00:00
2022-12-14 22:22:11 +00:00
( simple_key_simple_value_pairs, simple_key_serialisable_value_pairs, serialisable_key_simple_value_pairs, serialisable_key_serialisable_value_pairs ) = old_serialisable_info
2015-10-07 21:56:22 +00:00
2022-12-14 22:22:11 +00:00
meta_keys_and_meta_values = []
2015-10-07 21:56:22 +00:00
2022-12-14 22:22:11 +00:00
for ( key, value ) in simple_key_simple_value_pairs:
meta_keys_and_meta_values.append( ( ( META_SERIALISABLE_TYPE_JSON_OK, key ), ( META_SERIALISABLE_TYPE_JSON_OK, value ) ) )
2015-10-07 21:56:22 +00:00
2022-12-14 22:22:11 +00:00
for ( key, serialisable_value ) in simple_key_serialisable_value_pairs:
2021-01-20 22:22:03 +00:00
2022-12-14 22:22:11 +00:00
meta_keys_and_meta_values.append( ( ( META_SERIALISABLE_TYPE_JSON_OK, key ), ( META_SERIALISABLE_TYPE_HYDRUS_SERIALISABLE, serialisable_value ) ) )
2021-01-20 22:22:03 +00:00
2022-12-14 22:22:11 +00:00
for ( serialisable_key, value ) in serialisable_key_simple_value_pairs:
2021-01-20 22:22:03 +00:00
2022-12-14 22:22:11 +00:00
meta_keys_and_meta_values.append( ( ( META_SERIALISABLE_TYPE_HYDRUS_SERIALISABLE, serialisable_key ), ( META_SERIALISABLE_TYPE_JSON_OK, value ) ) )
2021-01-20 22:22:03 +00:00
2022-12-14 22:22:11 +00:00
for ( serialisable_key, serialisable_value ) in serialisable_key_serialisable_value_pairs:
2021-01-20 22:22:03 +00:00
2022-12-14 22:22:11 +00:00
meta_keys_and_meta_values.append( ( ( META_SERIALISABLE_TYPE_HYDRUS_SERIALISABLE, serialisable_key ), ( META_SERIALISABLE_TYPE_HYDRUS_SERIALISABLE, serialisable_value ) ) )
2021-01-20 22:22:03 +00:00
2015-10-07 21:56:22 +00:00
2022-12-14 22:22:11 +00:00
new_serialisable_info = meta_keys_and_meta_values
return ( 2, new_serialisable_info )
2015-10-07 21:56:22 +00:00
def GetSerialisableTuple( self ):
# TODO: delete this around version 537
# this is a patch to deal with me foolishly updating SerialisableDictionary without thinking that it is used in network comms
# the server suddenly starts giving version 2 Dicts, and old clients can't handle it!
# therefore, we are patching this to give a version 1 result if we are the server. we don't transport bytes stuff over network yet, nor store bytes in server services dict, so it is ok
# we are doing this for version 511, so let's give lads ~26 weeks to update
if HC.RUNNING_SERVER:
serialisable_info = self._GetSerialisableInfoVersion1()
return ( self.SERIALISABLE_TYPE, 1, serialisable_info )
else:
if hasattr( self, '_lock' ):
with getattr( self, '_lock' ):
serialisable_info = self._GetSerialisableInfo()
else:
serialisable_info = self._GetSerialisableInfo()
return ( self.SERIALISABLE_TYPE, self.SERIALISABLE_VERSION, serialisable_info )
2022-12-14 22:22:11 +00:00
2015-10-14 21:02:25 +00:00
SERIALISABLE_TYPES_TO_OBJECT_TYPES[ SERIALISABLE_TYPE_DICTIONARY ] = SerialisableDictionary
2017-03-02 02:14:56 +00:00
class SerialisableBytesDictionary( SerialisableBase, dict ):
SERIALISABLE_TYPE = SERIALISABLE_TYPE_BYTES_DICT
2017-11-29 21:48:23 +00:00
SERIALISABLE_NAME = 'Serialisable Dictionary With Bytestring Key/Value Support'
2017-03-02 02:14:56 +00:00
SERIALISABLE_VERSION = 1
def __init__( self, *args, **kwargs ):
dict.__init__( self, *args, **kwargs )
SerialisableBase.__init__( self )
def _GetSerialisableInfo( self ):
pairs = []
2022-12-14 22:22:11 +00:00
for ( key, value ) in self.items():
2017-03-02 02:14:56 +00:00
2017-03-15 20:13:04 +00:00
if isinstance( key, int ):
encoded_key = key
else:
2019-01-09 22:59:03 +00:00
encoded_key = key.hex()
2017-03-15 20:13:04 +00:00
2017-03-02 02:14:56 +00:00
if isinstance( value, ( list, tuple, set ) ):
2019-01-09 22:59:03 +00:00
encoded_value = [ item.hex() for item in value ]
2017-03-02 02:14:56 +00:00
2017-12-06 22:06:56 +00:00
elif value is None:
encoded_value = value
2017-03-02 02:14:56 +00:00
else:
2019-01-09 22:59:03 +00:00
encoded_value = value.hex()
2017-03-02 02:14:56 +00:00
pairs.append( ( encoded_key, encoded_value ) )
return pairs
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
for ( encoded_key, encoded_value ) in serialisable_info:
2017-03-15 20:13:04 +00:00
if isinstance( encoded_key, int ):
key = encoded_key
else:
2019-01-09 22:59:03 +00:00
key = bytes.fromhex( encoded_key )
2017-03-15 20:13:04 +00:00
2017-03-02 02:14:56 +00:00
if isinstance( encoded_value, ( list, tuple, set ) ):
2019-01-09 22:59:03 +00:00
value = [ bytes.fromhex( encoded_item ) for encoded_item in encoded_value ]
2017-03-02 02:14:56 +00:00
2017-12-06 22:06:56 +00:00
elif encoded_value is None:
value = encoded_value
2017-03-02 02:14:56 +00:00
else:
2019-01-09 22:59:03 +00:00
value = bytes.fromhex( encoded_value )
2017-03-02 02:14:56 +00:00
self[ key ] = value
SERIALISABLE_TYPES_TO_OBJECT_TYPES[ SERIALISABLE_TYPE_BYTES_DICT ] = SerialisableBytesDictionary
2015-10-14 21:02:25 +00:00
class SerialisableList( SerialisableBase, list ):
SERIALISABLE_TYPE = SERIALISABLE_TYPE_LIST
2017-11-29 21:48:23 +00:00
SERIALISABLE_NAME = 'Serialisable List'
2022-12-14 22:22:11 +00:00
SERIALISABLE_VERSION = 3
2015-10-14 21:02:25 +00:00
def __init__( self, *args, **kwargs ):
list.__init__( self, *args, **kwargs )
SerialisableBase.__init__( self )
def _GetSerialisableInfo( self ):
2022-12-14 22:22:11 +00:00
meta_tuples = []
2022-12-07 22:41:53 +00:00
for obj in self:
# after being caught out on a recursive legacy thing here, we now coerce. I'm 99.7% confident it can't hurt
# careful not to do it through for SerialisableBase--don't want to coerce a SerialisableBytesDict to a SerialisableDict
if isinstance( obj, list ) and not isinstance( obj, SerialisableBase ):
obj = SerialisableList( obj )
elif isinstance( obj, dict ) and not isinstance( obj, SerialisableBase ):
obj = SerialisableDictionary( obj )
2022-12-14 22:22:11 +00:00
meta_tuple = ConvertObjectToMetaSerialisableTuple( obj )
2022-12-07 22:41:53 +00:00
2022-12-14 22:22:11 +00:00
meta_tuples.append( meta_tuple )
2022-12-07 22:41:53 +00:00
2022-12-14 22:22:11 +00:00
return meta_tuples
2015-10-14 21:02:25 +00:00
def _InitialiseFromSerialisableInfo( self, serialisable_info ):
2021-01-20 22:22:03 +00:00
have_shown_load_error = False
2022-12-14 22:22:11 +00:00
meta_tuples = serialisable_info
for meta_tuple in meta_tuples:
2015-10-14 21:02:25 +00:00
2022-12-14 22:22:11 +00:00
try:
2021-01-20 22:22:03 +00:00
2022-12-14 22:22:11 +00:00
obj = ConvertMetaSerialisableTupleToObject( meta_tuple )
2021-01-20 22:22:03 +00:00
2022-12-14 22:22:11 +00:00
except HydrusExceptions.SerialisationException as e:
if not have_shown_load_error:
2022-12-07 22:41:53 +00:00
2022-12-14 22:22:11 +00:00
HydrusData.ShowText( 'An object in a list could not load. It has been discarded from the list. More may also have failed to load, but to stop error spam, they will go silently. Your client may be running on code versions behind its database. Depending on the severity of this error, you may need to rollback to a previous backup. If you have no backup, you may want to kill your hydrus process now to stop the cleansed list ever being saved back to the db.' )
HydrusData.ShowException( e )
2022-12-07 22:41:53 +00:00
2022-12-14 22:22:11 +00:00
have_shown_load_error = True
2021-01-20 22:22:03 +00:00
2022-12-14 22:22:11 +00:00
continue
2021-01-20 22:22:03 +00:00
self.append( obj )
2015-10-14 21:02:25 +00:00
2022-12-07 22:41:53 +00:00
def _UpdateSerialisableInfo( self, version, old_serialisable_info ):
if version == 1:
serialised_objects = old_serialisable_info
is_serialised = True
new_serialisable_info = [ ( is_serialised, serialised_object ) for serialised_object in serialised_objects ]
return ( 2, new_serialisable_info )
2022-12-14 22:22:11 +00:00
if version == 2:
meta_items = []
serialised_object_tuples = old_serialisable_info
for ( is_serialised, serialised_obj ) in serialised_object_tuples:
if is_serialised:
meta_items.append( ( META_SERIALISABLE_TYPE_HYDRUS_SERIALISABLE, serialised_obj ) )
else:
meta_items.append( ( META_SERIALISABLE_TYPE_JSON_OK, serialised_obj ) )
new_serialisable_info = meta_items
return ( 3, new_serialisable_info )
2022-12-07 22:41:53 +00:00
2017-01-25 22:56:55 +00:00
SERIALISABLE_TYPES_TO_OBJECT_TYPES[ SERIALISABLE_TYPE_LIST ] = SerialisableList