Source code for cis_interface.metaschema.properties.ScalarMetaschemaProperties
import numpy as np
from cis_interface import units, backwards, platform
from cis_interface.metaschema.datatypes import MetaschemaTypeError
from cis_interface.metaschema.properties import register_metaschema_property
from cis_interface.metaschema.properties.MetaschemaProperty import MetaschemaProperty
from collections import OrderedDict
_valid_numpy_types = ['int', 'uint', 'float', 'complex']
_valid_types = OrderedDict([(k, k) for k in _valid_numpy_types])
_flexible_types = ['string', 'bytes', 'unicode']
_python_scalars = OrderedDict([('float', [float]),
('int', [int]), ('uint', []),
('complex', [complex])])
if backwards.PY2: # pragma: Python 2
from __builtin__ import unicode
_valid_numpy_types += ['string', 'unicode']
_valid_types['bytes'] = 'string'
_valid_types['unicode'] = 'unicode'
_python_scalars.update([('bytes', [str]), ('unicode', [unicode])])
else: # pragma: Python 3
_valid_numpy_types += ['bytes', 'unicode', 'str']
_valid_types['bytes'] = 'bytes'
_valid_types['unicode'] = 'str'
_python_scalars.update([('bytes', [bytes]), ('unicode', [str])])
for t, t_np in _valid_types.items():
prec_list = []
if t in ['float']:
prec_list = [16, 32, 64]
if not platform._is_win:
prec_list.append(128) # Not available on windows
if t in ['int', 'uint']:
prec_list = [8, 16, 32, 64]
elif t in ['complex']:
prec_list = [64, 128]
if not platform._is_win:
prec_list.append(256) # Not available on windows
if hasattr(np, t_np):
_python_scalars[t].append(getattr(np, t_np))
_python_scalars[t].append(np.dtype(t_np).type)
for p in prec_list:
_python_scalars[t].append(np.dtype(t_np + str(p)).type)
# For some reason windows fails to check types on ints in some cases
_python_scalars['int'].append(np.signedinteger)
_python_scalars['uint'].append(np.unsignedinteger)
_all_python_scalars = [units._unit_quantity]
for k in _python_scalars.keys():
_python_scalars[k].append(units._unit_quantity)
_all_python_scalars += list(_python_scalars[k])
_python_scalars[k] = tuple(_python_scalars[k])
_all_python_arrays = tuple(set([np.ndarray, units._unit_array]))
_all_python_scalars = tuple(set(_all_python_scalars))
[docs]def data2dtype(data):
r"""Get numpy data type for an object.
Args:
data (object): Python object.
Returns:
np.dtype: Numpy data type.
"""
data_nounits = units.get_data(data)
if isinstance(data_nounits, np.ndarray):
dtype = data_nounits.dtype
elif isinstance(data_nounits, (list, dict, tuple)):
raise MetaschemaTypeError
elif isinstance(data_nounits, np.dtype(_valid_types['bytes']).type):
dtype = np.array(data_nounits).dtype
else:
dtype = np.array([data_nounits]).dtype
return dtype
[docs]def definition2dtype(props):
r"""Get numpy data type for a type definition.
Args:
props (dict): Type definition properties.
Returns:
np.dtype: Numpy data type.
"""
typename = props.get('subtype', None)
if typename is None:
typename = props.get('type', None)
if typename is None:
raise KeyError('Could not find type in dictionary')
if ('precision' not in props):
if typename in _flexible_types:
out = np.dtype((_valid_types[typename]))
else:
raise RuntimeError("Precision required for type: '%s'" % typename)
elif typename == 'unicode':
out = np.dtype((_valid_types[typename], int(props['precision'] // 32)))
elif typename in _flexible_types:
out = np.dtype((_valid_types[typename], int(props['precision'] // 8)))
else:
out = np.dtype('%s%d' % (_valid_types[typename], int(props['precision'])))
return out
[docs]@register_metaschema_property
class SubtypeMetaschemaProperty(MetaschemaProperty):
r"""Property class for 'subtype' property."""
name = 'subtype'
schema = {'description': 'The base type for each item.',
'type': 'string',
'enum': [k for k in sorted(_valid_types.keys())]}
[docs] @classmethod
def encode(cls, instance, typedef=None):
r"""Encoder for the 'subtype' scalar property."""
dtype = data2dtype(instance)
out = None
for k, v in _valid_types.items():
if dtype.name.startswith(v):
out = k
break
if out is None:
raise MetaschemaTypeError('Cannot find subtype string for dtype %s'
% dtype)
return out
[docs] @classmethod
def normalize_in_schema(cls, schema):
r"""Normalization for the 'subtype' scalar property in a schema."""
if cls.name in schema:
return schema
if not units.is_null_unit(schema.get('units', '')):
schema.setdefault(cls.name, 'float')
return schema
[docs]@register_metaschema_property
class PrecisionMetaschemaProperty(MetaschemaProperty):
r"""Property class for 'precision' property."""
name = 'precision'
schema = {'description': 'The size (in bits) of each item.',
'type': 'number',
'minimum': 1}
[docs] @classmethod
def encode(cls, instance, typedef=None):
r"""Encoder for the 'precision' scalar property."""
dtype = data2dtype(instance)
out = dtype.itemsize * 8 # in bits
return out
[docs] @classmethod
def compare(cls, prop1, prop2, root1=None, root2=None):
r"""Comparison for the 'precision' scalar property."""
if (prop1 > prop2):
yield '%s is greater than %s' % (prop1, prop2)
[docs] @classmethod
def normalize_in_schema(cls, schema):
r"""Normalization for the 'precision' scalar property in a schema."""
if cls.name in schema:
return schema
subtype = schema.get('subtype', None)
if subtype in ['float', 'int', 'uint']:
schema.setdefault(cls.name, int(64))
elif subtype in ['complex']:
schema.setdefault(cls.name, int(128))
return schema
[docs]@register_metaschema_property
class UnitsMetaschemaProperty(MetaschemaProperty):
r"""Property class for 'units' property."""
name = 'units'
schema = {'description': 'Physical units.',
'type': 'string'}
[docs] @classmethod
def encode(cls, instance, typedef=None):
r"""Encoder for the 'units' scalar property."""
out = units.get_units(instance)
if (not out) and (typedef is not None):
out = typedef
return out
[docs] @classmethod
def compare(cls, prop1, prop2, root1=None, root2=None):
r"""Comparision for the 'units' scalar property."""
if not units.are_compatible(prop1, prop2):
yield "Unit '%s' is not compatible with unit '%s'" % (prop1, prop2)