Skip to content

Commit 64ccb59

Browse files
committed
- Update tests
- Fix bug with nullable lists (e.g. `boards[].drivers`) - Add timeout to non intercept connections
1 parent 9bd3f6f commit 64ccb59

20 files changed

+3160
-256
lines changed

.vscode/settings.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
{
22
"python.testing.unittestEnabled": false,
3-
"python.testing.pytestEnabled": true
3+
"python.testing.pytestEnabled": true,
4+
"python.testing.unittestArgs": [
5+
"-v",
6+
"-s",
7+
"./tests",
8+
"-p",
9+
"test_*.py"
10+
],
11+
"debugpy.debugJustMyCode": false
412
}

examples/subscribe_object_model.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,17 @@ def subscribe():
1313
subscribe_connection = SubscribeConnection(SubscriptionMode.PATCH)
1414
subscribe_connection.connect()
1515

16-
try:
17-
# Get the complete model once
18-
object_model = subscribe_connection.get_object_model()
19-
print(object_model)
20-
21-
# Get multiple incremental updates, due to SubscriptionMode.PATCH, only a
22-
# subset of the object model will be updated
23-
for _ in range(0, 3):
24-
update = subscribe_connection.get_object_model_patch()
25-
object_model.update_from_json(update)
26-
print(update)
27-
except Exception as e:
28-
print(f"An error occurred: {e}")
29-
finally:
30-
subscribe_connection.close()
16+
# Get the complete model once
17+
object_model = subscribe_connection.get_object_model()
18+
print(object_model)
19+
20+
# Get multiple incremental updates, due to SubscriptionMode.PATCH, only a
21+
# subset of the object model will be updated
22+
for _ in range(0, 3):
23+
update = subscribe_connection.get_object_model_patch()
24+
object_model.update_from_json(update)
25+
print(update)
26+
subscribe_connection.close()
3127

3228

3329
if __name__ == "__main__":

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ pytest
22
wheel
33
build
44
twine
5-
bump2version
5+
bump2version
6+
python-dateutil

src/dsf/connections/base_connection.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import socket
3+
import time
34
from typing import Optional
45

56
from .exceptions import IncompatibleVersionException, InternalServerException, TaskCanceledException
@@ -25,7 +26,8 @@ def connect(self, init_message: client_init_messages.ClientInitMessage, socket_f
2526

2627
self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
2728
self.socket.connect(socket_file)
28-
self.socket.setblocking(True)
29+
self.socket.settimeout(self.timeout if self.timeout > 0 else None)
30+
# self.socket.setblocking(True)
2931
server_init_msg = server_init_message.ServerInitMessage.from_json(
3032
json.loads(self.socket.recv(50).decode("utf8"))
3133
)
@@ -98,7 +100,11 @@ def receive_json(self) -> str:
98100
json_string = json_string[:end_index]
99101
else:
100102
found = False
103+
start_time = time.time()
101104
while not found:
105+
if (self.timeout > 0) and (time.time() - start_time > self.timeout):
106+
raise TimeoutError("Timeout while waiting for JSON response")
107+
102108
# Refill the buffer and check again
103109
BUFF_SIZE = 4096 # 4 KiB
104110
data = b""

src/dsf/connections/intercept_connection.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ def __init__(
3737
auto_evaluate_expression: bool = True,
3838
priority_codes: bool = False,
3939
debug: bool = False,
40+
timeout: int = 0
4041
):
41-
super().__init__(debug)
42+
super().__init__(debug, timeout)
4243
self.interception_mode = interception_mode
4344
self.channels = channels if channels is not None else CodeChannel.list()
4445
self.filters = filters

src/dsf/object_model/model_collection.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,14 @@ def __init__(self, item_constructor: Type[T], value: Optional[List[T]] = None) -
1919
self._item_constructor: Type[T] = item_constructor
2020

2121
if value is not None:
22-
self[:] = value
22+
self[:] = []
23+
for (i, item) in enumerate(value):
24+
if isinstance(item, self._item_constructor):
25+
self.append(item)
26+
else:
27+
ref_item = self._item_constructor()
28+
ref_item.update_from_json(item)
29+
self.append(ref_item)
2330

2431
def update_from_json(self, json_element: List[Any]) -> 'ModelCollection[T]':
2532
"""

src/dsf/object_model/utils.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Type, TypeVar, Any, Union, Dict
2+
13

24
def is_model_object(o):
35
from .model_object import ModelObject
@@ -7,7 +9,10 @@ def is_model_object(o):
79
return isinstance(o, ModelObject) or isinstance(o, ModelCollection) or isinstance(o, ModelDictionary)
810

911

10-
def wrap_model_property(name, model_type):
12+
T = TypeVar('T') # Type variable for model objects
13+
14+
15+
def wrap_model_property(name: str, model_type: Type[T]) -> Union[Type[T], None]:
1116
"""
1217
Wrap a nullable model object property so that type checks can be performed during update
1318
:param name: Property of the derived class
@@ -18,11 +23,11 @@ def wrap_model_property(name, model_type):
1823
STORAGE_NAME = '_' + name
1924

2025
@property
21-
def prop(self):
26+
def prop(self) -> Union[Type[T], None]:
2227
return getattr(self, STORAGE_NAME)
2328

2429
@prop.setter
25-
def prop(self, value):
30+
def prop(self, value: Union[Type[T], str, Dict[str, Any], None]):
2631
if value is None or isinstance(value, model_type):
2732
setattr(self, STORAGE_NAME, value)
2833
elif isinstance(value, dict): # Update from JSON

tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)