130 lines
3.6 KiB
Python
130 lines
3.6 KiB
Python
import re
|
|
import copy
|
|
import operator
|
|
from collections import abc
|
|
from functools import reduce
|
|
from contextlib import contextmanager
|
|
|
|
|
|
def bottom_level_name(obj):
|
|
return obj.__name__.split(".")[-1]
|
|
|
|
|
|
def description_from_view(view_class):
|
|
methods = []
|
|
for method_key in ["get", "post", "put", "delete"]:
|
|
if hasattr(view_class, method_key):
|
|
methods.append(method_key.upper())
|
|
brief_description = get_docstring(view_class).partition("\n")[0].strip()
|
|
|
|
d = {"methods": methods, "description": brief_description}
|
|
|
|
return d
|
|
|
|
|
|
def get_docstring(obj):
|
|
ds = obj.__doc__
|
|
if ds:
|
|
return ds.strip()
|
|
else:
|
|
return ""
|
|
|
|
|
|
def camel_to_snake(name):
|
|
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
|
|
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower()
|
|
|
|
|
|
def camel_to_spine(name):
|
|
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1-\2", name)
|
|
return re.sub("([a-z0-9])([A-Z])", r"\1-\2", s1).lower()
|
|
|
|
|
|
@contextmanager
|
|
def set_properties(obj, **kwargs):
|
|
"""A context manager to set, then reset, certain properties of an object.
|
|
|
|
The first argument is the object, subsequent keyword arguments are properties
|
|
of said object, which are set initially, then reset to their previous values.
|
|
"""
|
|
saved_properties = {}
|
|
for k in kwargs.keys():
|
|
try:
|
|
saved_properties[k] = getattr(obj, k)
|
|
except AttributeError:
|
|
print(
|
|
"Warning: could not get {} on {}. This property will not be restored!".format(
|
|
k, obj
|
|
)
|
|
)
|
|
for k, v in kwargs.items():
|
|
setattr(obj, k, v)
|
|
try:
|
|
yield
|
|
finally:
|
|
for k, v in saved_properties.items():
|
|
setattr(obj, k, v)
|
|
|
|
|
|
def axes_to_array(
|
|
coordinate_dictionary, axis_keys=("x", "y", "z"), base_array=None, asint=True
|
|
):
|
|
"""Takes key-value pairs of a JSON value, and maps onto an array"""
|
|
# If no base array is given
|
|
if not base_array:
|
|
# Create an array of zeros
|
|
base_array = [0] * len(axis_keys)
|
|
else:
|
|
# Create a copy of the passed base_array
|
|
base_array = copy.copy(base_array)
|
|
|
|
# Do the mapping
|
|
for axis, key in enumerate(axis_keys):
|
|
if key in coordinate_dictionary:
|
|
base_array[axis] = (
|
|
int(coordinate_dictionary[key]) if asint else coordinate_dictionary[key]
|
|
)
|
|
|
|
return base_array
|
|
|
|
|
|
def filter_dict(dictionary: dict, keys: list):
|
|
# Get value by recursively applying getitem
|
|
val = reduce(operator.getitem, keys, dictionary)
|
|
|
|
# Create new dictionary by running reduce on key, val pairs
|
|
out = reduce(lambda x, y: {y: x}, reversed(keys), val)
|
|
|
|
return out
|
|
|
|
|
|
def entry_by_id(entry_id: str, object_list: list):
|
|
"""Return an object from a list, if <object>.id matches id argument."""
|
|
found = None
|
|
for o in object_list:
|
|
if o.id == entry_id:
|
|
found = o
|
|
return found
|
|
|
|
|
|
def recursively_apply(data, func):
|
|
"""
|
|
Recursively apply a function to a dictionary, list, array, or tuple
|
|
|
|
Args:
|
|
data: Input iterable data
|
|
func: Function to apply to all non-iterable values
|
|
"""
|
|
# If the object is a dictionary
|
|
if isinstance(data, abc.Mapping):
|
|
return {key: recursively_apply(val, func) for key, val in data.items()}
|
|
# If the object is iterable but NOT a dictionary or a string
|
|
elif (
|
|
isinstance(data, abc.Iterable)
|
|
and not isinstance(data, abc.Mapping)
|
|
and not isinstance(data, str)
|
|
):
|
|
return [recursively_apply(x, func) for x in data]
|
|
# if the object is neither a map nor iterable
|
|
else:
|
|
return func(data)
|