# -*- coding: utf-8 -*-
import json
from collections import OrderedDict
from graphenebase.types import (
Uint8,
Int16,
Uint16,
Uint32,
Uint64,
Varint32,
Int64,
String,
Bytes,
Void,
Array,
PointInTime,
Signature,
Bool,
Set,
Fixed_array,
Optional,
Static_variant,
Map,
Id,
VoteId,
ObjectId,
JsonObj,
)
from .chains import known_chains
from .objecttypes import object_type
from .account import PublicKey
from .chains import default_prefix
from .operationids import operations
class Operation(list):
""" The superclass for an operation. This class i used to instanciate an
operation, identify the operationid/name and serialize the operation
into bytes.
"""
module = "graphenebase.operations"
fromlist = ["operations"]
operations = operations
def __init__(self, op, **kwargs):
list.__init__(self, [0, GrapheneObject()])
# Are we dealing with an actual operation as list of opid and payload?
if isinstance(op, list) and len(op) == 2:
self._setidanename(op[0])
self.set(**op[1])
# Here, we allow to only load the Operation as Template without data
elif isinstance(op, str) or isinstance(op, int):
self._setidanename(op)
if kwargs:
self.set(**kwargs)
elif isinstance(op, GrapheneObject):
self._loadGrapheneObject(op)
else:
raise ValueError("Unknown format for Operation({})".format(type(op)))
@property
def id(self):
return self[0]
@id.setter
def id(self, value):
assert isinstance(value, int)
self[0] = value
@property
def operation(self):
return self[1]
@operation.setter
def operation(self, value):
assert isinstance(value, dict)
self[1] = value
@property
def op(self):
return self[1]
[docs] def set(self, **data):
try:
klass = self.klass()
except Exception: # pragma: no cover
raise NotImplementedError("Unimplemented Operation %s" % self.name)
self.operation = klass(**data)
def _setidanename(self, identifier):
if isinstance(identifier, int):
self.id = int(identifier)
self.name = self.getOperationNameForId(self.id)
else:
assert identifier in self.ops
self.id = self.getOperationIdForName(identifier)
self.name = identifier
@property
def opId(self):
return self.id
@property
def klass_name(self):
return self.name[0].upper() + self.name[1:] # klassname
def _loadGrapheneObject(self, op):
assert isinstance(op, GrapheneObject)
self.operation = op
self.name = op.__class__.__name__.lower()
self.id = self.getOperationIdForName(self.name)
def __bytes__(self):
return bytes(Id(self.id)) + bytes(self.op)
def __str__(self):
return json.dumps(self.__json__())
def __json__(self):
return [self.id, self.op.json()]
def _getklass(self, name):
module = __import__(self.module, fromlist=self.fromlist)
class_ = getattr(module, name)
return class_
[docs] def klass(self):
return self._getklass(self.klass_name)
@property
def ops(self):
if callable(self.operations): # pragma: no cover
# Legacy support
return self.operations()
else:
return self.operations
[docs] def getOperationIdForName(self, name):
return self.ops[name]
[docs] def getOperationNameForId(self, i):
""" Convert an operation id into the corresponding string
"""
for key in self.ops:
if int(self.ops[key]) is int(i):
return key
raise ValueError("Unknown Operation ID %d" % i)
toJson = __json__
json = __json__
class GrapheneObject(OrderedDict):
""" Core abstraction class
This class is used for any JSON reflected object in Graphene.
* ``instance.__json__()``: encodes data into json format
* ``bytes(instance)``: encodes data into wire format
* ``str(instances)``: dumps json object as string
"""
def __init__(self, *args, **kwargs):
if len(args) == 1 and isinstance(args[0], self.__class__):
# In this case, there is only one argument which is already an
# instance of a class that inherits Graphene Object, hence, we copy
# data and are done
# This basic allows to do
#
# Asset(Asset(amount=1, asset_id="1.3.0"))
self.data = args[0].data.copy()
return
if len(args) == 1 and isinstance(args[0], (dict, OrderedDict)):
if hasattr(self, "detail"):
super().__init__(self.detail(**args[0]))
else:
OrderedDict.__init__(self, args[0])
return
elif kwargs and hasattr(self, "detail"):
# If I receive kwargs, I need detail() implemented!
super().__init__(self.detail(*args, **kwargs))
def __bytes__(self):
if len(self) == 0:
return bytes()
b = b""
for name, value in self.items():
if isinstance(value, str):
b += bytes(value, "utf-8")
else:
b += bytes(value)
return b
def __json__(self):
if len(self) == 0:
return {}
d = {} # JSON output is *not* ordered
for name, value in self.items():
if isinstance(value, Optional) and value.isempty(): # pragma: no cover
continue
if isinstance(value, String):
d.update({name: str(value)})
else:
try:
d.update({name: JsonObj(value)})
except Exception:
d.update({name: value.__str__()})
return d
def __str__(self):
return json.dumps(self.__json__())
# Legacy support
@property
def data(self): # pragma: no cover
""" Read data explicitly (backwards compatibility)
"""
return self
@data.setter
def data(self, data): # pragma: no cover
""" Set data through a setter (backwards compatibility)
"""
self.update(data)
toJson = __json__
json = __json__
# Legacy
def isArgsThisClass(self, args):
return len(args) == 1 and type(args[0]).__name__ == type(self).__name__
# Common Objects
class Asset(GrapheneObject):
def detail(self, *args, **kwargs):
return OrderedDict(
[
("amount", Int64(kwargs["amount"])),
("asset_id", ObjectId(kwargs["asset_id"], "asset")),
]
)