Skip to content

Commit

Permalink
Add typing to CiscoInterface().as_set() and CiscoInterface().as_list()
Browse files Browse the repository at this point in the history
  • Loading branch information
mpenning committed Oct 16, 2023
1 parent c7c398e commit b19c458
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 34 deletions.
47 changes: 36 additions & 11 deletions ciscoconfparse/ccp_util.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
r""" ccp_util.py - Parse, Query, Build, and Modify IOS-style configurations
Copyright (C) 2021-2023 David Michael Pennington
Copyright (C) 2023 David Michael Pennington
Copyright (C) 2020-2021 David Michael Pennington at Cisco Systems
Copyright (C) 2019-2020 David Michael Pennington at ThousandEyes
Copyright (C) 2014-2019 David Michael Pennington at Samsung Data Services
Expand Down Expand Up @@ -2973,6 +2973,7 @@ def reverse_dns_lookup(input_str, timeout=3.0, server="4.2.2.2", proto="udp"):
class CiscoInterface(object):
interface_name = None
interface_dict = None
debug = None
_prefix = None
_digit_separator = None
_slot = None
Expand Down Expand Up @@ -3014,6 +3015,7 @@ def __init__(self, interface_name=None, interface_dict=None, debug=False):

self.interface_name = interface_name
self.interface_dict = interface_dict
self.debug = debug

if isinstance(interface_name, str):
intf_dict = self.parse_single_interface(interface_name, debug=debug)
Expand Down Expand Up @@ -3242,6 +3244,14 @@ def parse_intf_long(self, re_intf_long=None, debug=False):
_slot = groupdict_slot_card_port["slot"]
_card = groupdict_slot_card_port["card"]
_port = groupdict_slot_card_port["port"]

# Handle Ethernet1/48, where 48 is initially assigned to
# _card (should be port)
if isinstance(_slot, str) and isinstance(_card, str) and _port is None:
# Swap _card and _port
_port = _card
_card = None

if isinstance(_slot, str):
_slot = int(_slot)
if isinstance(_card, str):
Expand Down Expand Up @@ -3406,15 +3416,15 @@ def number(self):
logger.error(error)
# Use sys.exit(1) here to avoid infinite recursion during
# pathological errors such as a dash in an interface range
logger.critical("Exit on `CiscoInterface()` failure to avoid infinite recursion during raise ValueError()")
logger.critical(f"Exit on `CiscoInterface(interface_name='{self.interface_name}')`. Manually calling sys.exit(99) on this failure to avoid infinite recursion during raise ValueError(). This might be a bug in a third-party library.")
sys.exit(99)

# Fix regex port parsing problems... relocate _slot and _card, as-required
if self._slot is None and self._card is None and isinstance(self._port, int):
if self._slot is None and self._card is None and isinstance(self._port, (int, str)):
self._number = f"{self._port}"
elif isinstance(self._slot, int) and self._card is None and isinstance(self._port, int):
elif isinstance(self._slot, (int, str)) and self._card is None and isinstance(self._port, (int, str)):
self._number = f"{self._slot}{self.digit_separator}{self._port}"
elif isinstance(self._slot, int) and isinstance(self._card, int) and isinstance(self._port, int):
elif isinstance(self._slot, (int, str)) and isinstance(self._card, (int, str)) and isinstance(self._port, (int, str)):
self._number = f"{self._slot}{self.digit_separator}{self._card}{self.digit_separator}{self._port}"
else:
error = f"Could not parse into _number: _slot: {self._slot} _card: {self._card} _port: {self._port} _digit_separator: {self.digit_separator}"
Expand Down Expand Up @@ -3452,10 +3462,17 @@ def digit_separator(self):
def digit_separator(self, value):
if isinstance(value, str):
self._digit_separator = value
else:
elif self.slot is not None or self.card is not None:
error = f"Could not set _digit_separator: {value} {type(value)}"
logger.critical(error)
raise ValueError(error)
elif self.slot is None and self.card is None:
if self.debug is True:
logger.debug(f"Ignoring _digit_separator when there is no slot or card.")
else:
error = f"Found an unsupported value for `CiscoInterface().digit_separator`."
logger.error(error)
raise ValueError(error)

@property
@logger.catch(reraise=True)
Expand Down Expand Up @@ -3879,7 +3896,6 @@ def remove(self, arg):
return self

# This method is on CiscoRange()
@property
@logger.catch(reraise=True)
def as_list(self, result_type=str):
"""Return a list of sorted components; an empty string is automatically rejected. This method is tricky to test due to the requirement for the `.sort_list` attribute on all elements; avoid using the ordered nature of `as_list` and use `as_set`."""
Expand All @@ -3903,7 +3919,7 @@ def as_list(self, result_type=str):
elif result_type is int:
return [int(ii) for ii in retval]
else:
error f"CiscoRange().as_list(result_type={result_type}) is not valid. Choose from {[None, int, str]}. result_type: None will return CiscoInterface() objects."
error = f"CiscoRange().as_list(result_type={result_type}) is not valid. Choose from {[None, int, str]}. result_type: None will return CiscoInterface() objects."
logger.critical(error)
raise ValueError(error)
except AttributeError as eee:
Expand All @@ -3915,11 +3931,20 @@ def as_list(self, result_type=str):
raise ValueError(eee)

# This method is on CiscoRange()
@property
@logger.catch(reraise=True)
def as_set(self, result_type=str):
"""Return an unsorted set({}) components. Use this method instead of `.as_list` whenever possible to avoid the requirement for elements needing a `.sort_list` attribute."""
return set(self._list)
retval = set(self._list)
if result_type is None:
return retval
elif result_type is str:
return set([str(ii) for ii in retval])
elif result_type is int:
return set([int(ii) for ii in retval])
else:
error = f"CiscoRange().as_list(result_type={result_type}) is not valid. Choose from {[None, int, str]}. result_type: None will return CiscoInterface() objects."
logger.critical(error)
raise ValueError(error)

# This method is on CiscoRange()
## Github issue #125
Expand Down Expand Up @@ -3988,7 +4013,7 @@ def compressed_str(self):
elif result_type is int:
return [int(ii) for ii in retval]
else:
error f"CiscoRange().as_set(result_type={result_type}) is not valid. Choose from {[None, int, str]}. result_type: None will return CiscoInterface() objects."
error = f"CiscoRange().as_set(result_type={result_type}) is not valid. Choose from {[None, int, str]}. result_type: None will return CiscoInterface() objects."
logger.critical(error)
raise ValueError(error)

Expand Down
34 changes: 17 additions & 17 deletions tests/test_Ccp_Util.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ def test_CiscoRange_01():
result_correct = {"1", "2", "3"}
uut_str = "1-3"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct
assert CiscoRange(uut_str).iterate_attribute == "port"


Expand All @@ -734,7 +734,7 @@ def test_CiscoRange_02():
result_correct = {"1", "3"}
uut_str = "1,3"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct
assert CiscoRange(uut_str).iterate_attribute == "port"


Expand All @@ -749,7 +749,7 @@ def test_CiscoRange_03():
}
uut_str = "1,2-4,5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct
assert CiscoRange(uut_str).iterate_attribute == "port"


Expand All @@ -758,7 +758,7 @@ def test_CiscoRange_04():
result_correct = {"1", "2", "3", "4", "5"}
uut_str = "1-3,4,5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct
assert CiscoRange(uut_str).iterate_attribute == "port"


Expand All @@ -767,7 +767,7 @@ def test_CiscoRange_05():
result_correct = {"1", "2", "3", "4", "5"}
uut_str = "1,2,3-5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct
assert CiscoRange(uut_str).iterate_attribute == "port"


Expand All @@ -776,7 +776,7 @@ def test_CiscoRange_06():
result_correct = {"1/1", "1/2", "1/3", "1/4", "1/5"}
uut_str = "1/1-3,4,5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct
assert CiscoRange(uut_str).iterate_attribute == "port"


Expand All @@ -785,39 +785,39 @@ def test_CiscoRange_07():
result_correct = {"1/1", "1/2", "1/3", "1/4", "1/5"}
uut_str = "1/1,2-4,5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


def test_CiscoRange_08():
"""Basic slot range test"""
result_correct = {"1/1", "1/2", "1/3", "1/4", "1/5"}
uut_str = "1/1,2,3-5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


def test_CiscoRange_09():
"""Basic slot range test"""
result_correct = {"2/1/1", "2/1/2", "2/1/3", "2/1/4", "2/1/5"}
uut_str = "2/1/1-3,4,5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


def test_CiscoRange_10():
"""Basic slot range test"""
result_correct = {"2/1/1", "2/1/2", "2/1/3", "2/1/4", "2/1/5"}
uut_str = "2/1/1,2-4,5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


def test_CiscoRange_11():
"""Basic slot range test"""
result_correct = {"2/1/1", "2/1/2", "2/1/3", "2/1/4", "2/1/5"}
uut_str = "2/1/1,2,3-5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


if False:
Expand All @@ -826,31 +826,31 @@ def test_CiscoRange_12():
result_correct = {"2/1/1", "2/1/2", "2/1/3", "2/1/4", "2/1/5"}
uut_str = "interface Eth2/1/1-3,4,5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


def test_CiscoRange_13():
"""Basic interface slot range test"""
result_correct = {"2/1/1", "2/1/2", "2/1/3", "2/1/4", "2/1/5"}
uut_str = "interface Eth2/1/1,2-4,5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


def test_CiscoRange_14():
"""Basic interface slot range test"""
result_correct = {"2/1/1", "2/1/2", "2/1/3", "2/1/4", "2/1/5"}
uut_str = "interface Eth2/1/1,2,3-5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


def test_CiscoRange_15():
"""Basic interface slot range test"""
result_correct = {"2/1/1", "2/1/2", "2/1/3", "2/1/4", "2/1/5"}
uut_str = "interface Eth 2/1/1,2,3-5"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


def test_CiscoRange_18():
Expand All @@ -862,7 +862,7 @@ def test_CiscoRange_18():
}
uut_str = "Eth1/1,Eth1/12-20,Eth1/16,Eth1/10"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


if False:
Expand All @@ -876,7 +876,7 @@ def test_CiscoRange_19():
}
uut_str = "interface Eth1/1,interface Eth1/12-20,interface Eth1/16,interface Eth1/10"
assert isinstance(uut_str, str)
assert CiscoRange(uut_str).as_set == result_correct
assert CiscoRange(uut_str).as_set(result_type=str) == result_correct


def test_CiscoRange_compressed_str_01():
Expand Down
13 changes: 7 additions & 6 deletions tests/test_Models_Cisco.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

r""" test_Models_Cisco.py - Parse, Query, Build, and Modify IOS-style configs
Copyright (C) 2023 David Michael Pennington
Copyright (C) 2020-2021 David Michael Pennington at Cisco Systems
Copyright (C) 2019 David Michael Pennington at ThousandEyes
Copyright (C) 2014-2019 David Michael Pennington at Samsung Data Services
Expand Down Expand Up @@ -280,8 +281,8 @@ def testVal_IOSIntfLine_trunk_vlan_allowed_01():
]
cfg = CiscoConfParse(lines, factory=True)
intf_obj = cfg.find_objects("^interface")[0]
assert len(intf_obj.trunk_vlans_allowed.as_set) == len(range(1, 4095))
assert intf_obj.trunk_vlans_allowed.as_set == {str(ii) for ii in range(1, 4095)}
assert len(intf_obj.trunk_vlans_allowed.as_set(result_type=str)) == len(range(1, 4095))
assert intf_obj.trunk_vlans_allowed.as_set(result_type=str) == {str(ii) for ii in range(1, 4095)}


def testVal_IOSIntfLine_trunk_vlan_allowed_02():
Expand All @@ -295,7 +296,7 @@ def testVal_IOSIntfLine_trunk_vlan_allowed_02():
]
cfg = CiscoConfParse(lines, factory=True)
intf_obj = cfg.find_objects("^interface")[0]
assert intf_obj.trunk_vlans_allowed.as_set == {"2", "4", "6", "911"}
assert intf_obj.trunk_vlans_allowed.as_set(result_type=str) == {"2", "4", "6", "911"}


def testVal_IOSIntfLine_trunk_vlan_allowed_03():
Expand All @@ -310,7 +311,7 @@ def testVal_IOSIntfLine_trunk_vlan_allowed_03():
]
cfg = CiscoConfParse(lines, factory=True)
intf_obj = cfg.find_objects("^interface")[0]
assert intf_obj.trunk_vlans_allowed.as_set == {"4", "6", "911"}
assert intf_obj.trunk_vlans_allowed.as_set(result_type=str) == {"4", "6", "911"}


def testVal_IOSIntfLine_trunk_vlan_allowed_04():
Expand All @@ -325,7 +326,7 @@ def testVal_IOSIntfLine_trunk_vlan_allowed_04():
]
cfg = CiscoConfParse(lines, factory=True)
intf_obj = cfg.find_objects("^interface")[0]
assert intf_obj.trunk_vlans_allowed.as_set == {"1"}
assert intf_obj.trunk_vlans_allowed.as_set(result_type=str) == {"1"}


def testVal_IOSIntfLine_trunk_vlan_allowed_05():
Expand All @@ -340,7 +341,7 @@ def testVal_IOSIntfLine_trunk_vlan_allowed_05():
]
cfg = CiscoConfParse(lines, factory=True)
intf_obj = cfg.find_objects("^interface")[0]
assert intf_obj.trunk_vlans_allowed.as_set == {"1",}
assert intf_obj.trunk_vlans_allowed.as_set(result_type=str) == {"1",}


def testVal_IOSIntfLine_trunk_vlan_allowed_06():
Expand Down

0 comments on commit b19c458

Please sign in to comment.