"""
|
|
#lookup.py
|
|
|
|
This file contains all the mappings between hickle/HDF5 metadata and python types.
|
|
There are four dictionaries and one set that are populated here:
|
|
|
|
1) types_dict
|
|
types_dict: mapping between python types and dataset creation functions, e.g.
|
|
types_dict = {
|
|
list: create_listlike_dataset,
|
|
int: create_python_dtype_dataset,
|
|
np.ndarray: create_np_array_dataset
|
|
}
|
|
|
|
2) hkl_types_dict
|
|
hkl_types_dict: mapping between hickle metadata and dataset loading functions, e.g.
|
|
hkl_types_dict = {
|
|
"<type 'list'>" : load_list_dataset,
|
|
"<type 'tuple'>" : load_tuple_dataset
|
|
}
|
|
|
|
3) container_types_dict
|
|
container_types_dict: mapping required to convert the PyContainer object in hickle.py
|
|
back into the required native type. PyContainer is required as
|
|
some iterable types are immutable (do not have an append() function).
|
|
Here is an example:
|
|
container_types_dict = {
|
|
"<type 'list'>": list,
|
|
"<type 'tuple'>": tuple
|
|
}
|
|
|
|
4) container_key_types_dict
|
|
container_key_types_dict: mapping specifically for converting hickled dict data back into
|
|
a dictionary with the same key type. While python dictionary keys
|
|
can be any hashable object, in HDF5 a unicode/string is required
|
|
for a dataset name. Example:
|
|
container_key_types_dict = {
|
|
"<type 'str'>": str,
|
|
"<type 'unicode'>": unicode
|
|
}
|
|
|
|
5) types_not_to_sort
|
|
type_not_to_sort is a list of hickle type attributes that may be hierarchical,
|
|
but don't require sorting by integer index.
|
|
|
|
## Extending hickle to add support for other classes and types
|
|
|
|
The process to add new load/dump capabilities is as follows:
|
|
|
|
1) Create a file called load_[newstuff].py in loaders/
|
|
2) In the load_[newstuff].py file, define your create_dataset and load_dataset functions,
|
|
along with all required mapping dictionaries.
|
|
3) Add an import call here, and populate the lookup dictionaries with update() calls:
|
|
# Add loaders for [newstuff]
|
|
try:
|
|
from .loaders.load_[newstuff[ import types_dict as ns_types_dict
|
|
from .loaders.load_[newstuff[ import hkl_types_dict as ns_hkl_types_dict
|
|
types_dict.update(ns_types_dict)
|
|
hkl_types_dict.update(ns_hkl_types_dict)
|
|
... (Add container_types_dict etc if required)
|
|
except ImportError:
|
|
raise
|
|
"""
|
|
|
|
import six
|
|
import pkg_resources
|
|
|
|
|
|
def return_first(x):
|
|
""" Return first element of a list """
|
|
return x[0]
|
|
|
|
|
|
def load_nothing(h_hode):
|
|
pass
|
|
|
|
|
|
types_dict = {}
|
|
hkl_types_dict = {}
|
|
types_not_to_sort = [b'dict', b'csr_matrix', b'csc_matrix', b'bsr_matrix']
|
|
|
|
container_types_dict = {
|
|
b"<type 'list'>": list,
|
|
b"<type 'tuple'>": tuple,
|
|
b"<type 'set'>": set,
|
|
b"<class 'list'>": list,
|
|
b"<class 'tuple'>": tuple,
|
|
b"<class 'set'>": set,
|
|
b"csr_matrix": return_first,
|
|
b"csc_matrix": return_first,
|
|
b"bsr_matrix": return_first
|
|
}
|
|
|
|
# Technically, any hashable object can be used, for now sticking with built-in types
|
|
container_key_types_dict = {
|
|
b"<type 'str'>": str,
|
|
b"<type 'float'>": float,
|
|
b"<type 'bool'>": bool,
|
|
b"<type 'int'>": int,
|
|
b"<type 'complex'>": complex,
|
|
b"<class 'str'>": str,
|
|
b"<class 'float'>": float,
|
|
b"<class 'bool'>": bool,
|
|
b"<class 'int'>": int,
|
|
b"<class 'complex'>": complex
|
|
}
|
|
|
|
if six.PY2:
|
|
container_key_types_dict[b"<type 'unicode'>"] = unicode
|
|
container_key_types_dict[b"<type 'long'>"] = long
|
|
|
|
# Add loaders for built-in python types
|
|
if six.PY2:
|
|
from .loaders.load_python import types_dict as py_types_dict
|
|
from .loaders.load_python import hkl_types_dict as py_hkl_types_dict
|
|
else:
|
|
from .loaders.load_python3 import types_dict as py_types_dict
|
|
from .loaders.load_python3 import hkl_types_dict as py_hkl_types_dict
|
|
|
|
types_dict.update(py_types_dict)
|
|
hkl_types_dict.update(py_hkl_types_dict)
|
|
|
|
# Add loaders for numpy types
|
|
from .loaders.load_numpy import types_dict as np_types_dict
|
|
from .loaders.load_numpy import hkl_types_dict as np_hkl_types_dict
|
|
from .loaders.load_numpy import check_is_numpy_array
|
|
types_dict.update(np_types_dict)
|
|
hkl_types_dict.update(np_hkl_types_dict)
|
|
|
|
|
|
#####################
|
|
# ND-ARRAY checking #
|
|
#####################
|
|
|
|
ndarray_like_check_fns = [
|
|
check_is_numpy_array
|
|
]
|
|
|
|
|
|
def check_is_ndarray_like(py_obj):
|
|
is_ndarray_like = False
|
|
for ii, check_fn in enumerate(ndarray_like_check_fns):
|
|
is_ndarray_like = check_fn(py_obj)
|
|
if is_ndarray_like:
|
|
break
|
|
return is_ndarray_like
|
|
|
|
|
|
#####################
|
|
# loading optional #
|
|
#####################
|
|
|
|
def register_class(myclass_type, hkl_str, dump_function, load_function,
|
|
to_sort=True, ndarray_check_fn=None):
|
|
""" Register a new hickle class.
|
|
|
|
Args:
|
|
myclass_type type(class): type of class
|
|
dump_function (function def): function to write data to HDF5
|
|
load_function (function def): function to load data from HDF5
|
|
hkl_str (str): String to write to HDF5 file to describe class
|
|
to_sort (bool): If the item is iterable, does it require sorting?
|
|
ndarray_check_fn (function def): function to use to check if
|
|
|
|
"""
|
|
types_dict.update({myclass_type: dump_function})
|
|
hkl_types_dict.update({hkl_str: load_function})
|
|
if to_sort == False:
|
|
types_not_to_sort.append(hkl_str)
|
|
if ndarray_check_fn is not None:
|
|
ndarray_like_check_fns.append(ndarray_check_fn)
|
|
|
|
|
|
def register_class_list(class_list):
|
|
""" Register multiple classes in a list
|
|
|
|
Args:
|
|
class_list (list): A list, where each item is an argument to
|
|
the register_class() function.
|
|
|
|
Notes: This just runs the code:
|
|
for item in mylist:
|
|
register_class(*item)
|
|
"""
|
|
for class_item in class_list:
|
|
register_class(*class_item)
|
|
|
|
|
|
def register_class_exclude(hkl_str_to_ignore):
|
|
""" Tell loading funciton to ignore any HDF5 dataset with attribute 'type=XYZ'
|
|
|
|
Args:
|
|
hkl_str_to_ignore (str): attribute type=string to ignore and exclude from loading.
|
|
"""
|
|
hkl_types_dict[hkl_str_to_ignore] = load_nothing
|
|
|
|
|
|
def register_exclude_list(exclude_list):
|
|
""" Ignore HDF5 datasets with attribute type='XYZ' from loading
|
|
|
|
ArgsL
|
|
exclude_list (list): List of strings, which correspond to hdf5/hickle
|
|
type= attributes not to load.
|
|
"""
|
|
for hkl_str in exclude_list:
|
|
register_class_exclude(hkl_str)
|
|
|
|
|
|
######################
|
|
# Scipy sparse array #
|
|
######################
|
|
|
|
try:
|
|
pkg_resources.require('hickle[scipy]')
|
|
from .loaders.load_scipy import class_register, exclude_register
|
|
register_class_list(class_register)
|
|
register_exclude_list(exclude_register)
|
|
except pkg_resources.DistributionNotFound:
|
|
pass
|
|
|
|
##################
|
|
# Astropy stuff #
|
|
##################
|
|
|
|
try:
|
|
pkg_resources.require('hickle[astropy]')
|
|
from .loaders.load_astropy import class_register
|
|
register_class_list(class_register)
|
|
except pkg_resources.DistributionNotFound:
|
|
pass
|
|
|
|
################
|
|
# Pandas stuff #
|
|
################
|
|
|
|
try:
|
|
pkg_resources.require('hickle[pandas]')
|
|
from .loaders.load_pandas import class_register
|
|
register_class_list(class_register)
|
|
except pkg_resources.DistributionNotFound:
|
|
pass
|