from typing import Any
from typing import Dict
from typing import List
from typing import Optional
from typing import Type
from typing import Union
from typing import cast
from pymavryk.context.abstract import AbstractContext
from pymavryk.michelson.micheline import Micheline
from pymavryk.michelson.micheline import MichelsonRuntimeError
from pymavryk.michelson.types import OrType
from pymavryk.michelson.types.adt import wrap_or
from pymavryk.michelson.types.adt import wrap_parameters
from pymavryk.michelson.types.base import MichelsonType
from pymavryk.michelson.types.base import parse_name
from pymavryk.michelson.types.core import Unit
[docs]class ParameterSection(Micheline, prim='parameter', args_len=1):
args: List[Type[MichelsonType]] # type: ignore
root_name: str
def __init__(self, item: MichelsonType):
super().__init__()
self.item = item
def __repr__(self):
return repr(self.item)
[docs] @staticmethod
def match(type_expr) -> Type['ParameterSection']:
try:
cls = Micheline.match(type_expr)
if not issubclass(cls, ParameterSection):
cls = ParameterSection.create_type(args=[cls])
except Exception as e:
raise MichelsonRuntimeError('parameter', *e.args) from e
return cls
[docs] @classmethod
def create_type(
cls,
args: List[Union[Type['Micheline'], Any]],
annots: Optional[list] = None,
**kwargs,
) -> Type['ParameterSection']:
assert not annots, 'top level parameter annotations not supported'
root_type = cast(Type[MichelsonType], args[0])
if issubclass(root_type, OrType):
root_name = root_type.field_name # type: ignore
if not root_name:
flat_args = root_type.get_flat_args(entrypoints=True) # type: ignore
assert isinstance(flat_args, dict), f'expected a named type, got {flat_args}'
root_name = 'root' if 'default' in flat_args else 'default'
else:
root_name = root_type.field_name or 'default'
res = type(cls.__name__, (cls,), dict(args=args, root_name=root_name, **kwargs))
return cast(Type['ParameterSection'], res)
[docs] @classmethod
def execute(cls, stack, stdout: List[str], context: AbstractContext):
context.set_parameter_expr(cls.as_micheline_expr())
stdout.append(f'parameter: updated')
[docs] @classmethod
def list_entrypoints(cls) -> Dict[str, Type[MichelsonType]]:
entrypoints = {}
root_type = cls.args[0]
if issubclass(root_type, OrType):
flat_args = root_type.get_flat_args(entrypoints=True)
assert isinstance(flat_args, dict), f'expected dict of named entrypoints'
for name, arg in flat_args.items():
entrypoints[name] = arg.get_anon_type()
entrypoints[cls.root_name] = root_type
return entrypoints
[docs] @classmethod
def from_parameters(cls, parameters: Dict[str, Any]) -> 'ParameterSection':
if len(parameters) == 0:
parameters = {'entrypoint': 'default', 'value': {'prim': 'Unit'}}
assert isinstance(parameters, dict) and parameters.keys() == {
'entrypoint',
'value',
}, f'expected {{entrypoint, value}}, got {parameters}'
entrypoint = parameters['entrypoint']
if entrypoint == cls.root_name:
res = cls.from_micheline_value(parameters['value'])
return cast(ParameterSection, res)
else:
root_type = cls.args[0]
assert issubclass(root_type, OrType), f'expected `{cls.root_name}`, got `{entrypoint}`'
_, key_to_path, _ = root_type.get_type_layout(entrypoints=True)
assert entrypoint in key_to_path, f'unexpected entrypoint `{entrypoint}`' # type: ignore
val_expr = wrap_parameters(parameters['value'], key_to_path[entrypoint]) # type: ignore
item = root_type.from_micheline_value(val_expr)
return cls(item)
[docs] def to_parameters(self, mode='readable') -> Dict[str, Any]:
entrypoint, item = self.root_name, self.item
if isinstance(self.item, OrType):
flat_values = self.item.get_flat_values(entrypoints=True)
assert isinstance(flat_values, dict) and len(flat_values) == 1, f'expected named type'
entrypoint, item = next(iter(flat_values.items()))
return {
'entrypoint': entrypoint,
'value': item.to_micheline_value(mode=mode, lazy_diff=None),
}
[docs] @classmethod
def generate_pydoc(cls) -> str:
definitions = [] # type: ignore
res = cls.args[0].generate_pydoc(definitions, cls.prim)
if res != f'${cls.prim}':
definitions.insert(0, (cls.prim, res))
return '\n'.join(f'${var}:\n\t{doc}\n' for var, doc in definitions)
[docs] @classmethod
def from_micheline_value(cls, val_expr) -> 'ParameterSection':
item = cls.args[0].from_micheline_value(val_expr)
return cls(item)
[docs] @classmethod
def from_python_object(cls, py_obj) -> 'ParameterSection':
if isinstance(py_obj, str):
entrypoint = py_obj
py_obj = {entrypoint: Unit}
else:
if not isinstance(py_obj, dict) or len(py_obj) != 1:
raise TypeError(f'expected dict with a single key, got {type(py_obj).__name__} `{py_obj}`')
entrypoint = next(iter(py_obj))
if entrypoint == cls.root_name:
item = cls.args[0].from_python_object(py_obj[entrypoint])
else:
if not issubclass(cls.args[0], OrType):
raise TypeError(f'Unexpected entrypoint `{entrypoint}`: parameter is not of sum type')
_, key_to_path, _ = cls.args[0].get_type_layout(infer_names=True, entrypoints=True)
if not key_to_path:
raise TypeError('sum type has to be named (in the scope of PyMavryk)')
item = cls.args[0].from_python_object(wrap_or(py_obj[entrypoint], key_to_path[entrypoint]))
return cls(item)
[docs] def to_micheline_value(self, mode='readable', lazy_diff=None):
return self.item.to_micheline_value(mode=mode, lazy_diff=None)
[docs] def to_python_object(self, try_unpack=False, lazy_diff=None) -> dict:
py_obj = self.item.to_python_object(
try_unpack=try_unpack,
lazy_diff=None,
)
if issubclass(self.args[0], OrType):
return py_obj
else:
return {self.root_name: py_obj}
[docs] def merge_lazy_diff(self, lazy_diff: List[dict]) -> 'ParameterSection':
item = self.item.merge_lazy_diff(lazy_diff)
return type(self)(item)
[docs] def attach_context(self, context: AbstractContext):
self.item.attach_context(context, big_map_copy=True)
[docs] def aggregate_lazy_diff(self, mode='readable') -> List[dict]:
lazy_diff = [] # type: ignore
self.item.aggregate_lazy_diff(lazy_diff, mode=mode)
return lazy_diff
def __getitem__(self, key):
assert hasattr(self.item, '__getitem__'), f'index access is not implemented for {self.item.prim}'
return self.item[key]