diff --git a/.gitignore b/.gitignore index 399a111..73c8c0f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf +.vscode/ # Generated files .idea/**/contentModel.xml diff --git a/scripts/convert.py b/scripts/convert.py index 935a152..35169ba 100755 --- a/scripts/convert.py +++ b/scripts/convert.py @@ -57,7 +57,7 @@ def get_mongo_storage_by_session(client, *args, **kwargs): while True: try: char = raw_input("Please enter a number or enter for all: ") - if char is "": + if char == "": return client.find(*args, **kwargs) return client.find_by_session_id(s_lut[int(char)]["id"], *args, **kwargs) except (EOFError, ValueError, IndexError): diff --git a/src/topic_store/api.py b/src/topic_store/api.py index 9f20bd5..68d4762 100644 --- a/src/topic_store/api.py +++ b/src/topic_store/api.py @@ -5,20 +5,11 @@ import pathlib from abc import ABCMeta, abstractmethod, abstractproperty +from .utils import abstractstatic __all__ = ["load", "Storage"] -class abstract_static(staticmethod): - __slots__ = () - - def __init__(self, function): - super(abstract_static, self).__init__(function) - function.__isabstractmethod__ = True - - __isabstractmethod__ = True - - class Storage: __metaclass__ = ABCMeta @@ -26,7 +17,7 @@ class Storage: def suffix(self): raise NotImplementedError - @abstract_static + @abstractstatic def load(path): """Storage containers must have a load mechanism"""'' raise NotImplementedError diff --git a/src/topic_store/data.py b/src/topic_store/data.py index 9cc7497..7c489ef 100644 --- a/src/topic_store/data.py +++ b/src/topic_store/data.py @@ -12,6 +12,7 @@ import roslib.message import rospy from genpy import Message as ROSMessage +from .utils import cached_property try: from collections import Mapping as MappingType @@ -211,18 +212,29 @@ def __init__(self, data_tree): if not isinstance(data_tree, dict): raise ValueError("Data tree must be a dict to construct a TopicStore") # Ensure passed data tree does not contain ROS msgs - self.__data_tree = DefaultTypeParser()(data_tree) - if "_id" not in self.__data_tree: - self.__data_tree["_id"] = bson.ObjectId() - if "_ts_meta" not in self.__data_tree: - self.__data_tree["_ts_meta"] = dict(session=_session_id, sys_time=time_as_ms(), ros_time=ros_time_as_ms()) + self.data_tree = DefaultTypeParser()(data_tree) + if "_id" not in self.data_tree: + self.data_tree["_id"] = bson.ObjectId() + if "_ts_meta" not in self.data_tree: + self.data_tree["_ts_meta"] = dict(session=_session_id, sys_time=time_as_ms(), ros_time=ros_time_as_ms()) # Cache for dict to ROS message parsing self.__msgs = None @property - def dict(self): + def data_tree(self): return self.__data_tree + @data_tree.setter + def data_tree(self, value): + # clears variable cache + del self.to_ros_msg_list + del self.flatten_ros_msg_dict + self.__data_tree = value + + @property + def dict(self): + return self.data_tree + @property def msgs(self): if self.__msgs is None: @@ -418,10 +430,10 @@ def __ros_msg_dict_to_list(ros_msg_dict, return_keys=False, parent=""): for ret in TopicStore.__ros_msg_dict_to_list(value, return_keys, key if not parent else parent + "." + key): yield ret + @cached_property def to_ros_msg_list(self): - # TODO: Cache this operation until self.__data_tree updated return list(TopicStore.__ros_msg_dict_to_list(self.msgs)) + @cached_property def flatten_ros_msg_dict(self): - # TODO: Cache this operation until self.__data_tree updated return {k: v for k, v in TopicStore.__ros_msg_dict_to_list(self.msgs, return_keys=True)} diff --git a/src/topic_store/utils.py b/src/topic_store/utils.py new file mode 100644 index 0000000..51c8e41 --- /dev/null +++ b/src/topic_store/utils.py @@ -0,0 +1,32 @@ +# Peter Lightbody (pet1330) Copyright (c) 2020 +# Email: pet1330@gmail.com + + +"""A decorator to cache instance variables""" +class cached_property(property): + def __init__(self, method): + self.method = method + + def __set__(self, obj, value): + obj.__dict__[self.method.__name__] = value + + def __delete__(self, obj): + del obj.__dict__[self.method.__name__] + + def __get__(self, obj, type=None): + if obj is None: + return self + + if not self.method.__name__ in obj.__dict__: + obj.__dict__[self.method.__name__] = self.method(obj) + return obj.__dict__[self.method.__name__] + + +class abstractstatic(staticmethod): + __slots__ = () + + def __init__(self, function): + super(abstractstatic, self).__init__(function) + function.__isabstractmethod__ = True + + __isabstractmethod__ = True