Skip to content

Commit 261a44e

Browse files
authored
resolve issue/69 (#76)
* resolve issue/69 * add class and methods documentation
1 parent fe19317 commit 261a44e

File tree

3 files changed

+343
-101
lines changed

3 files changed

+343
-101
lines changed

hololinked/server/json_storage.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import os
2+
import threading
3+
from .serializers import JSONSerializer
4+
from typing import Any, Dict, List, Optional, Union
5+
from .property import Property
6+
from ..param import Parameterized
7+
8+
9+
class ThingJsonStorage:
10+
"""
11+
JSON-based storage engine composed within ``Thing``. Carries out property operations such as storing and
12+
retrieving values from a plain JSON file.
13+
14+
Parameters
15+
----------
16+
filename : str
17+
Path to the JSON file to use for storage.
18+
instance : Parameterized
19+
The ``Thing`` instance which uses this storage. Required to read default property values when
20+
creating missing properties.
21+
serializer : JSONSerializer, optional
22+
Serializer used for encoding and decoding JSON data. Defaults to an instance of ``JSONSerializer``.
23+
"""
24+
def __init__(self, filename: str, instance: Parameterized, serializer: Optional[Any]=None):
25+
self.filename = filename
26+
self.thing_instance = instance
27+
self.instance_name = instance.instance_name
28+
self._serializer = serializer or JSONSerializer()
29+
self._lock = threading.RLock()
30+
self._data = self._load()
31+
32+
def _load(self) -> Dict[str, Any]:
33+
"""
34+
Load and decode data from the JSON file.
35+
36+
Returns
37+
-------
38+
value: dict
39+
A dictionary of all stored properties. Empty if the file does not exist or cannot be decoded.
40+
"""
41+
if not os.path.exists(self.filename) or os.path.getsize(self.filename) == 0:
42+
return {}
43+
try:
44+
with open(self.filename, 'rb') as f:
45+
raw_bytes = f.read()
46+
if not raw_bytes:
47+
return {}
48+
return self._serializer.loads(raw_bytes)
49+
except Exception:
50+
return {}
51+
52+
def _save(self):
53+
"""
54+
Encode and write data to the JSON file.
55+
"""
56+
raw_bytes = self._serializer.dumps(self._data)
57+
with open(self.filename, 'wb') as f:
58+
f.write(raw_bytes)
59+
60+
def get_property(self, property: Union[str, Property]) -> Any:
61+
"""
62+
Fetch a single property.
63+
64+
Parameters
65+
----------
66+
property: str | Property
67+
string name or descriptor object
68+
69+
Returns
70+
-------
71+
value: Any
72+
property value
73+
"""
74+
name = property if isinstance(property, str) else property.name
75+
if name not in self._data:
76+
raise KeyError(f"property {name} not found in JSON storage")
77+
with self._lock:
78+
return self._data[name]
79+
80+
def set_property(self, property: Union[str, Property], value: Any) -> None:
81+
"""
82+
change the value of an already existing property.
83+
84+
Parameters
85+
----------
86+
property: str | Property
87+
string name or descriptor object
88+
value: Any
89+
value of the property
90+
"""
91+
name = property if isinstance(property, str) else property.name
92+
with self._lock:
93+
self._data[name] = value
94+
self._save()
95+
96+
def get_properties(self, properties: Dict[Union[str, Property], Any]) -> Dict[str, Any]:
97+
"""
98+
get multiple properties at once.
99+
100+
Parameters
101+
----------
102+
properties: List[str | Property]
103+
string names or the descriptor of the properties as a list
104+
105+
Returns
106+
-------
107+
value: Dict[str, Any]
108+
property names and values as items
109+
"""
110+
names = [key if isinstance(key, str) else key.name for key in properties.keys()]
111+
with self._lock:
112+
return {name: self._data.get(name) for name in names}
113+
114+
def set_properties(self, properties: Dict[Union[str, Property], Any]) -> None:
115+
"""
116+
change the values of already existing few properties at once
117+
118+
Parameters
119+
----------
120+
properties: Dict[str | Property, Any]
121+
string names or the descriptor of the property and any value as dictionary pairs
122+
"""
123+
with self._lock:
124+
for obj, value in properties.items():
125+
name = obj if isinstance(obj, str) else obj.name
126+
self._data[name] = value
127+
self._save()
128+
129+
def get_all_properties(self) -> Dict[str, Any]:
130+
"""
131+
read all properties of the ``Thing`` instance.
132+
"""
133+
with self._lock:
134+
return dict(self._data)
135+
136+
def create_missing_properties(self, properties: Dict[str, Property],
137+
get_missing_property_names: bool = False) -> Optional[List[str]]:
138+
"""
139+
create any and all missing properties of ``Thing`` instance
140+
141+
Parameters
142+
----------
143+
properties: Dict[str, Property]
144+
descriptors of the properties
145+
146+
Returns
147+
-------
148+
missing_props: List[str]
149+
list of missing properties if get_missing_property_names is True
150+
"""
151+
missing_props = []
152+
with self._lock:
153+
existing_props = self.get_all_properties()
154+
for name, new_prop in properties.items():
155+
if name not in existing_props:
156+
self._data[name] = getattr(self.thing_instance, new_prop.name)
157+
missing_props.append(name)
158+
self._save()
159+
if get_missing_property_names:
160+
return missing_props
161+
162+
163+
__all__ = [
164+
ThingJsonStorage.__name__,
165+
]

0 commit comments

Comments
 (0)