from . import types
from ..utility.fmap import fmap
from ..utility.strings import ensure_str_safe, ensure_str
from ..utility.visitation import dispatch
import collections, json, traceback
def _object_hook(kwds):
try:
clsname = kwds.pop('__class__')
except KeyError:
return kwds
try:
cls = types.__dict__[clsname]
except KeyError:
raise TypeError('No ICurry class named %s was found' % clsname)
try:
kwds = fmap(ensure_str_safe, kwds)
return cls(**kwds)
except Exception:
raise TypeError(
'Cannot construct %s.%s from arguments %r: %s'
% (cls.__module__, cls.__name__, kwds, traceback.format_exc())
)
[docs]
def get_decoder():
return json.JSONDecoder(object_hook=_object_hook)
[docs]
def loads(json, decoder=get_decoder()):
'''Load ICurry encoded as JSON.'''
icurry = decoder.decode(ensure_str(json))
return icurry
[docs]
def load(file, decoder=get_decoder()):
if isinstance(file, str):
with open(file, 'r', encoding='utf-8') as istream:
data = istream.read()
return loads(data)
else:
return loads(file.read())
[docs]
def get_encoder(compact=True):
return Encoder(compact=compact)
[docs]
class Encoder(json.JSONEncoder):
'''
Encodes ICurry as JSON.
The compact form, the default, has no space after a comma or a colon. Both
forms escape every character outside ASCII. The toolchain writes the
compact form into the ``.json.z`` caches.
'''
def __init__(self, compact=True, **kwds):
if compact:
kwds.setdefault('separators', (',', ':'))
json.JSONEncoder.__init__(self, **kwds)
@dispatch.on('obj')
def default(self, obj):
return json.JSONEncoder.default(self, obj)
KEY_MAP = {
'filename' : None
, 'fullname' : 'name'
, 'IModule' : 'IProg'
, 'IDataType' : 'IType'
, 'symbolname': 'name'
}
VALUE_MAP = {
'aliases' : lambda v: list(v)
, 'functions': lambda v: list(v.values())
, 'imports' : lambda v: list(v)
, 'types' : lambda v: list(v.values())
}
[docs]
@default.when(types.IObject)
def default(self, iobj):
data = collections.OrderedDict()
clsname = type(iobj).__name__
data['__class__'] = self.KEY_MAP.get(clsname, clsname)
for k in getattr(iobj, '_fields_', iobj.__dict__):
v = getattr(iobj, k)
vmapper = self.VALUE_MAP.get(k)
k = self.KEY_MAP.get(k, k)
if k is not None:
if vmapper is not None:
v = vmapper(v)
data[k] = v
return data
_encoders = {}
[docs]
def dumps(icurry, compact=True):
'''Dump ICurry as JSON. See :class:`Encoder` for the two forms.'''
encoder = _encoders.get(compact)
if encoder is None:
encoder = _encoders[compact] = get_encoder(compact)
json = encoder.encode(icurry)
return ensure_str(json)
[docs]
def dump(icurry, file, compact=True):
if isinstance(file, str):
with open(file, 'w', encoding='utf-8') as ostream:
ostream.write(dumps(icurry, compact))
else:
file.write(dumps(icurry, compact))