diff --git a/src/contracting/constants.py b/src/contracting/constants.py index 93908189..9be548d7 100644 --- a/src/contracting/constants.py +++ b/src/contracting/constants.py @@ -5,7 +5,7 @@ DELIMITER = ':' INDEX_SEPARATOR = '.' HDF5_GROUP_SEPARATOR = '/' - +DEFAULT = '__default__' SUBMISSION_CONTRACT_NAME = 'submission' PRIVATE_METHOD_PREFIX = '__' EXPORT_DECORATOR_STRING = 'export' diff --git a/src/contracting/storage/orm.py b/src/contracting/storage/orm.py index 318f393d..18a97cec 100644 --- a/src/contracting/storage/orm.py +++ b/src/contracting/storage/orm.py @@ -1,6 +1,6 @@ from contracting.storage.driver import Driver from contracting.execution.runtime import rt -from contracting import constants +from contracting import constants as c from contracting.stdlib.bridge.decimal import ContractingDecimal driver = rt.env.get('__Driver') or Driver() @@ -35,28 +35,34 @@ def get(self): class Hash(Datum): def __init__(self, contract, name, driver: Driver = driver, default_value=None): super().__init__(contract, name, driver=driver) - self._delimiter = constants.DELIMITER self._default_value = default_value + # Store the default_value in storage if it's not None + if default_value is not None: + self._driver.set(f'{self._key}{c.DELIMITER}{c.DEFAULT}', default_value) + def _set(self, key, value): - self._driver.set(f'{self._key}{self._delimiter}{key}', value) + self._driver.set(f'{self._key}{c.DELIMITER}{key}', value) def _get(self, item): - value = self._driver.get(f'{self._key}{self._delimiter}{item}') + value = self._driver.get(f'{self._key}{c.DELIMITER}{item}') # Add Python defaultdict behavior for easier smart contracting if value is None: + # Retrieve the default_value from storage if not set + if self._default_value is None: + self._default_value = self._driver.get(f'{self._key}{c.DELIMITER}{c.DEFAULT}') value = self._default_value - if type(value) == float or type(value) == ContractingDecimal: + if isinstance(value, (float, ContractingDecimal)): return ContractingDecimal(str(value)) return value def _validate_key(self, key): if isinstance(key, tuple): - assert len(key) <= constants.MAX_HASH_DIMENSIONS, (f'Too many dimensions ({len(key)}) for hash. ' - f'Max is {constants.MAX_HASH_DIMENSIONS}') + assert len(key) <= c.MAX_HASH_DIMENSIONS, (f'Too many dimensions ({len(key)}) for hash. ' + f'Max is {c.MAX_HASH_DIMENSIONS}') new_key_str = '' for k in key: @@ -64,26 +70,26 @@ def _validate_key(self, key): k = str(k) - assert constants.DELIMITER not in k, 'Illegal delimiter in key.' - assert constants.INDEX_SEPARATOR not in k, 'Illegal separator in key.' + assert c.DELIMITER not in k, 'Illegal delimiter in key.' + assert c.INDEX_SEPARATOR not in k, 'Illegal separator in key.' - new_key_str += f'{k}{self._delimiter}' + new_key_str += f'{k}{c.DELIMITER}' - key = new_key_str[:-len(self._delimiter)] + key = new_key_str[:-len(c.DELIMITER)] else: key = str(key) - assert constants.DELIMITER not in key, 'Illegal delimiter in key.' - assert constants.INDEX_SEPARATOR not in key, 'Illegal separator in key.' + assert c.DELIMITER not in key, 'Illegal delimiter in key.' + assert c.INDEX_SEPARATOR not in key, 'Illegal separator in key.' - assert len(key) <= constants.MAX_KEY_SIZE, f'Key is too long ({len(key)}). Max is {constants.MAX_KEY_SIZE}.' + assert len(key) <= c.MAX_KEY_SIZE, f'Key is too long ({len(key)}). Max is {c.MAX_KEY_SIZE}.' return key def _prefix_for_args(self, args): multi = self._validate_key(args) - prefix = f'{self._key}{self._delimiter}' + prefix = f'{self._key}{c.DELIMITER}' if multi != '': - prefix += f'{multi}{self._delimiter}' + prefix += f'{multi}{c.DELIMITER}' return prefix @@ -128,6 +134,9 @@ def __init__(self, contract, name, foreign_contract, foreign_name, driver: Drive super().__init__(contract, name, driver=driver) self._key = self._driver.make_key(foreign_contract, foreign_name) + # Retrieve the default_value from the foreign Hash's storage + self._default_value = self._driver.get(f'{self._key}{c.DELIMITER}{c.DEFAULT}') + def _set(self, key, value): raise ReferenceError