Skip to content

Commit c3f4f20

Browse files
SIMPLE-7461-consolidate-exceptions-in-virl2-client (#144)
* SIMPLE-7461: initial consolidation of .*NotFound and ElementAlreadyExists exceptions * SIMPLE-7461: implemented fixes based on review * minor cleanup --------- Co-authored-by: Tomas Mikuska <[email protected]>
1 parent 787d404 commit c3f4f20

File tree

9 files changed

+37
-71
lines changed

9 files changed

+37
-71
lines changed

tests/test_image_upload.py

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -91,36 +91,26 @@ def windows_path(path: str):
9191

9292
@pytest.mark.parametrize(
9393
"test_path",
94-
[
95-
pytest.param(""),
96-
pytest.param("/"),
97-
pytest.param("./"),
98-
pytest.param("./../"),
99-
pytest.param("test/test/"),
100-
pytest.param("/test/test/"),
101-
pytest.param("\\"),
102-
pytest.param("..\\..\\"),
103-
pytest.param("\\test\\"),
104-
],
94+
["", "/", "./", "./../", "test/test/", "/test/test/", "\\", "..\\..\\", "\\test\\"],
10595
)
106-
@pytest.mark.parametrize("usage", [pytest.param("name"), pytest.param("rename")])
96+
@pytest.mark.parametrize("rename", [None, "rename"])
10797
@pytest.mark.parametrize(
108-
"test_string, message",
109-
[pytest.param(test_str, "wrong format") for test_str in WRONG_FORMAT_LIST]
110-
+ [pytest.param(test_str, "not supported") for test_str in NOT_SUPPORTED_LIST]
111-
+ [pytest.param(test_str, "") for test_str in EXPECTED_PASS_LIST],
98+
"test_string",
99+
WRONG_FORMAT_LIST + NOT_SUPPORTED_LIST + EXPECTED_PASS_LIST,
112100
)
113-
def test_image_upload_file(usage: str, test_string: str, message: str, test_path: str):
101+
def test_image_upload_file(rename: str, test_string: str, test_path: str):
114102
session = MagicMock()
115103
nid = NodeImageDefinitions(session)
116-
rename = None
117104
filename = test_path + test_string
118105

119-
if message in ("wrong format", "not supported"):
120-
with pytest.raises(InvalidImageFile, match=message):
106+
if test_string in WRONG_FORMAT_LIST:
107+
with pytest.raises(InvalidImageFile, match="wrong format"):
108+
with windows_path(filename):
109+
nid.upload_image_file(filename, rename)
110+
elif test_string in NOT_SUPPORTED_LIST:
111+
with pytest.raises(InvalidImageFile, match="unsupported extension"):
121112
with windows_path(filename):
122113
nid.upload_image_file(filename, rename)
123-
124114
elif test_path == "test_data/":
125115
with windows_path(filename):
126116
nid.upload_image_file(filename, rename)
@@ -132,7 +122,6 @@ def test_image_upload_file(usage: str, test_string: str, message: str, test_path
132122
assert isinstance(file, BufferedReader)
133123
assert pathlib.Path(file.name).resolve() == pathlib.Path(filename).resolve()
134124
file.close()
135-
136125
else:
137126
with pytest.raises(FileNotFoundError):
138127
with windows_path(filename):

virl2_client/exceptions.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,6 @@ class LabNotFound(ElementNotFound):
6060
pass
6161

6262

63-
class DesynchronizedError(VirlException):
64-
pass
65-
66-
6763
class InvalidContentType(VirlException):
6864
pass
6965

virl2_client/models/cl_pyats.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def _load_pyats_testbed(self, testbed_yaml: str) -> Testbed:
9191
Load a PyATS testbed instance from YAML representation.
9292
9393
Disable all templating features of PyATS markup processor.
94-
Also disable extensions loading (which still uses all of the templating)
94+
Also disable extensions loading (which still uses all the templating)
9595
https://pubhub.devnetcloud.com/media/pyats/docs/utilities/yaml_markup.html
9696
"""
9797
processor = _PyatsTMProcessor(

virl2_client/models/lab.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ def get_smart_annotation_by_tag(self, tag: str) -> SmartAnnotation:
566566
for annotation in self._smart_annotations.values():
567567
if tag == annotation.tag:
568568
return annotation
569-
raise SmartAnnotationNotFound
569+
raise SmartAnnotationNotFound(tag)
570570

571571
def find_nodes_by_tag(self, tag: str) -> list[Node]:
572572
"""
@@ -979,7 +979,7 @@ def create_interface(
979979
if desired_interface is None:
980980
# Shouldn't happen, but type checkers complain about desired_interface
981981
# possibly being None otherwise
982-
raise InterfaceNotFound
982+
raise InterfaceNotFound(node.id)
983983

984984
return desired_interface
985985

@@ -1385,7 +1385,7 @@ def _sync_topology(self, exclude_configurations: bool | None = False) -> None:
13851385
and f"Lab not found: {self._id}" in exc.response.text
13861386
):
13871387
self._stale = True
1388-
raise LabNotFound(f"Error syncing lab: {error_msg}")
1388+
raise LabNotFound(self._id)
13891389
raise
13901390

13911391
topology = result.json()
@@ -1456,7 +1456,7 @@ def _handle_import_nodes(self, topology: dict) -> None:
14561456
node_id = node["id"]
14571457

14581458
if node_id in self._nodes:
1459-
raise ElementAlreadyExists("Node already exists")
1459+
raise ElementAlreadyExists(node_id)
14601460

14611461
self._import_node(node_id, node)
14621462

@@ -1468,7 +1468,7 @@ def _handle_import_nodes(self, topology: dict) -> None:
14681468
iface_id = iface["id"]
14691469

14701470
if iface_id in self._interfaces:
1471-
raise ElementAlreadyExists("Interface already exists")
1471+
raise ElementAlreadyExists(iface_id)
14721472

14731473
self._import_interface(iface_id, node_id, iface)
14741474

@@ -1487,7 +1487,7 @@ def _handle_import_interfaces(self, topology: dict) -> None:
14871487
node_id = iface["node"]
14881488

14891489
if iface_id in self._interfaces:
1490-
raise ElementAlreadyExists("Interface already exists")
1490+
raise ElementAlreadyExists(iface_id)
14911491

14921492
self._import_interface(iface_id, node_id, iface)
14931493

@@ -1502,7 +1502,7 @@ def _handle_import_links(self, topology: dict) -> None:
15021502
link_id = link["id"]
15031503

15041504
if link_id in self._links:
1505-
raise ElementAlreadyExists("Link already exists")
1505+
raise ElementAlreadyExists(link_id)
15061506

15071507
iface_a_id = link["interface_a"]
15081508
iface_b_id = link["interface_b"]
@@ -1524,7 +1524,7 @@ def _handle_import_annotations(self, topology: dict) -> None:
15241524
annotation_id = annotation["id"]
15251525

15261526
if annotation_id in self._annotations:
1527-
raise ElementAlreadyExists("Annotation already exists")
1527+
raise ElementAlreadyExists(annotation_id)
15281528

15291529
self._import_annotation(annotation_id, annotation)
15301530

@@ -1535,7 +1535,7 @@ def _handle_import_annotations(self, topology: dict) -> None:
15351535
smart_annotation_id = smart_annotation["id"]
15361536

15371537
if smart_annotation_id in self._smart_annotations:
1538-
raise ElementAlreadyExists("Smart annotation already exists")
1538+
raise ElementAlreadyExists(smart_annotation_id)
15391539

15401540
self._import_smart_annotation(smart_annotation_id, smart_annotation)
15411541

@@ -1970,7 +1970,7 @@ def _find_link_in_topology(link_id: str, topology: dict) -> dict:
19701970
if link["id"] == link_id:
19711971
return link
19721972
# if it cannot be found, it is an internal structure error
1973-
raise LinkNotFound
1973+
raise LinkNotFound(link_id)
19741974

19751975
@staticmethod
19761976
def _find_interface_in_topology(interface_id: str, topology: dict) -> dict:
@@ -1990,7 +1990,7 @@ def _find_interface_in_topology(interface_id: str, topology: dict) -> dict:
19901990
if interface["id"] == interface_id:
19911991
return interface
19921992
# if it cannot be found, it is an internal structure error
1993-
raise InterfaceNotFound
1993+
raise InterfaceNotFound(interface_id)
19941994

19951995
@staticmethod
19961996
def _find_node_in_topology(node_id: str, topology: dict) -> dict:
@@ -2006,7 +2006,7 @@ def _find_node_in_topology(node_id: str, topology: dict) -> dict:
20062006
if node["id"] == node_id:
20072007
return node
20082008
# if it cannot be found, it is an internal structure error
2009-
raise NodeNotFound
2009+
raise NodeNotFound(node_id)
20102010

20112011
@staticmethod
20122012
def _find_annotation_in_topology(
@@ -2025,7 +2025,7 @@ def _find_annotation_in_topology(
20252025
if annotation["id"] == annotation_id:
20262026
return annotation
20272027
# if it cannot be found, it is an internal structure error
2028-
raise AnnotationNotFound
2028+
raise AnnotationNotFound(annotation_id)
20292029

20302030
@staticmethod
20312031
def _find_smart_annotation_in_topology(
@@ -2044,7 +2044,7 @@ def _find_smart_annotation_in_topology(
20442044
if annotation["id"] == annotation_id:
20452045
return annotation
20462046
# if it cannot be found, it is an internal structure error
2047-
raise SmartAnnotationNotFound
2047+
raise SmartAnnotationNotFound(annotation_id)
20482048

20492049
@check_stale
20502050
def get_pyats_testbed(self, hostname: str | None = None) -> str:

virl2_client/models/node.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ def get_interface_by_label(self, label: str) -> Interface:
578578
for iface in self.interfaces():
579579
if iface.label == label:
580580
return iface
581-
raise InterfaceNotFound(f"{label}:{self}")
581+
raise InterfaceNotFound(label)
582582

583583
@locked
584584
def get_interface_by_slot(self, slot: int) -> Interface:
@@ -592,7 +592,7 @@ def get_interface_by_slot(self, slot: int) -> Interface:
592592
for iface in self.interfaces():
593593
if iface.slot == slot:
594594
return iface
595-
raise InterfaceNotFound(f"{slot}:{self}")
595+
raise InterfaceNotFound(slot)
596596

597597
def get_links_to(self, other_node: Node) -> list[Link]:
598598
"""

virl2_client/models/node_image_definition.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,21 +226,20 @@ def upload_image_file(
226226

227227
if extension == "" or name == "":
228228
message = (
229-
f"Name specified ({name}) is in wrong format "
230-
f"(correct: filename.({'|'.join(extension_list)}) )."
229+
f"Specified filename ({name}) has wrong format "
230+
f"(correct format is filename.({'|'.join(extension_list)}) )."
231231
)
232232
raise InvalidImageFile(message)
233233

234234
if extension not in extension_list and last_ext not in extension_list:
235235
message = (
236-
f"Extension in {name} not supported. "
236+
f"Specified filename ({name}) has unsupported extension ({extension}) "
237237
f"(supported extensions are {', '.join(extension_list)})."
238238
)
239239
raise InvalidImageFile(message)
240240

241241
if not os.path.exists(filename):
242-
message = f"File with specified name ({filename}) does not exist."
243-
raise FileNotFoundError(message)
242+
raise FileNotFoundError(filename)
244243

245244
print(f"Uploading {name}")
246245
headers = {"X-Original-File-Name": name}

virl2_client/models/resource_pool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def get_resource_pools_by_ids(
112112
:param resource_pool_ids: A resource pool ID or an iterable of IDs.
113113
:returns: A ResourcePool object when one ID is passed, or a dictionary of IDs to
114114
either ResourcePools when a resource pool exists or None when it doesn't.
115-
:raises KeyError: When one ID is passed and it doesn't exist.
115+
:raises KeyError: When one ID is passed, and it doesn't exist.
116116
"""
117117
self.sync_resource_pools_if_outdated()
118118
if isinstance(resource_pool_ids, str):

virl2_client/utils.py

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,6 @@ def _make_not_found(instance: Element) -> ElementNotFound:
6060
class_name = type(instance).__name__
6161
if class_name.startswith("Annotation"):
6262
class_name = "Annotation"
63-
instance_id = instance._id
64-
if class_name == "Lab":
65-
instance_label = instance._title
66-
elif class_name.startswith("Annotation"):
67-
instance_label = instance._type
68-
else:
69-
instance_label = instance._label
70-
71-
error_text = (
72-
f"{class_name} {instance_label} ({instance_id}) no longer exists on the server."
73-
)
7463
error: Type[ElementNotFound] = {
7564
"Lab": LabNotFound,
7665
"Node": NodeNotFound,
@@ -79,15 +68,10 @@ def _make_not_found(instance: Element) -> ElementNotFound:
7968
"Annotation": AnnotationNotFound,
8069
"SmartAnnotation": SmartAnnotationNotFound,
8170
}[class_name]
82-
return error(error_text)
71+
return error(instance._id)
8372

8473

85-
def _check_and_mark_stale(
86-
func: Callable,
87-
instance: Element,
88-
*args,
89-
**kwargs,
90-
):
74+
def _check_and_mark_stale(func: Callable, instance: Element, *args, **kwargs):
9175
"""
9276
Check staleness before and after calling `func`
9377
and updates staleness if a 404 is raised.
@@ -107,7 +91,6 @@ def _check_and_mark_stale(
10791
if instance._stale:
10892
raise _make_not_found(instance)
10993
return ret
110-
11194
except httpx.HTTPStatusError as exc:
11295
resp = exc.response
11396
class_name = type(instance).__name__

virl2_client/virl2_client.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -596,8 +596,7 @@ def import_lab_from_path(self, path: str, title: str | None = None) -> Lab:
596596
"""
597597
topology_path = Path(path)
598598
if not topology_path.exists():
599-
message = f"{path} can not be found"
600-
raise FileNotFoundError(message)
599+
raise FileNotFoundError(path)
601600

602601
topology = topology_path.read_text()
603602
return self.import_lab(
@@ -789,7 +788,7 @@ def join_existing_lab(self, lab_id: str, sync_lab: bool = True) -> Lab:
789788
topology = self._session.get(url).json()
790789
except httpx.HTTPStatusError as exc:
791790
if exc.response.status_code == 404:
792-
raise LabNotFound("No lab with the given ID exists on the host.")
791+
raise LabNotFound(lab_id)
793792
raise
794793
title = topology.get("lab", {}).get("title")
795794
else:

0 commit comments

Comments
 (0)