-
Notifications
You must be signed in to change notification settings - Fork 32
Add custom exception hierarchy (#219) #239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,18 @@ | ||
| class DuplicateChildError(Exception): | ||
| class HierConfigError(Exception): | ||
| """Base exception for all hier_config errors.""" | ||
|
|
||
|
|
||
| class DuplicateChildError(HierConfigError): | ||
| """Raised when attempting to add a duplicate child.""" | ||
|
|
||
|
|
||
| class DriverNotFoundError(HierConfigError): | ||
| """Raised when a platform driver cannot be found.""" | ||
|
|
||
|
|
||
| class InvalidConfigError(HierConfigError): | ||
| """Raised for malformed configuration text.""" | ||
|
|
||
|
|
||
| class IncompatibleDriverError(HierConfigError): | ||
| """Raised when configs with mismatched drivers are used together.""" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| from collections.abc import Iterable | ||
| from logging import getLogger | ||
|
|
||
| from .exceptions import IncompatibleDriverError | ||
| from .models import TagRule | ||
| from .root import HConfig | ||
|
|
||
|
|
@@ -59,7 +60,7 @@ def __init__( | |
|
|
||
| if running_config.driver.__class__ is not generated_config.driver.__class__: | ||
| message = "The running and generated configs must use the same driver." | ||
| raise ValueError(message) | ||
| raise IncompatibleDriverError(message) | ||
|
Comment on lines
61
to
+63
|
||
|
|
||
| self._remediation_config: HConfig | None = None | ||
| self._rollback_config: HConfig | None = None | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| """Tests for the custom exception hierarchy (#219).""" | ||
|
|
||
| import pytest | ||
|
|
||
| from hier_config import Platform, WorkflowRemediation, get_hconfig, get_hconfig_driver | ||
| from hier_config.exceptions import ( | ||
| DriverNotFoundError, | ||
| DuplicateChildError, | ||
| HierConfigError, | ||
| IncompatibleDriverError, | ||
| InvalidConfigError, | ||
| ) | ||
|
|
||
|
|
||
| def test_hier_config_error_is_base_exception() -> None: | ||
| """All custom exceptions inherit from HierConfigError.""" | ||
| assert issubclass(DuplicateChildError, HierConfigError) | ||
| assert issubclass(DriverNotFoundError, HierConfigError) | ||
| assert issubclass(InvalidConfigError, HierConfigError) | ||
| assert issubclass(IncompatibleDriverError, HierConfigError) | ||
|
|
||
|
|
||
| def test_hier_config_error_is_catchable_as_exception() -> None: | ||
| """HierConfigError itself inherits from Exception.""" | ||
| assert issubclass(HierConfigError, Exception) | ||
|
|
||
|
|
||
| def test_duplicate_child_error_on_duplicate_section() -> None: | ||
| config = get_hconfig(Platform.CISCO_IOS, "interface Loopback0") | ||
| with pytest.raises(DuplicateChildError, match="Found a duplicate section"): | ||
| config.add_child("interface Loopback0") | ||
|
|
||
|
|
||
| def test_driver_not_found_error_invalid_platform() -> None: | ||
| """get_hconfig_driver raises DriverNotFoundError for unsupported platforms.""" | ||
| with pytest.raises(DriverNotFoundError, match="Unsupported platform"): | ||
| get_hconfig_driver("bogus_platform") # type: ignore[arg-type] | ||
|
|
||
|
|
||
| def test_incompatible_driver_error_mismatched_drivers() -> None: | ||
| """WorkflowRemediation raises IncompatibleDriverError for mismatched drivers.""" | ||
| running = get_hconfig(Platform.CISCO_IOS) | ||
| generated = get_hconfig(Platform.ARISTA_EOS) | ||
| with pytest.raises(IncompatibleDriverError, match="same driver"): | ||
| WorkflowRemediation(running, generated) | ||
|
|
||
|
|
||
| def test_invalid_config_error_banner_parsing() -> None: | ||
| """Malformed banner config raises InvalidConfigError.""" | ||
| config_text = "banner motd ^C\nthis banner never ends" | ||
| with pytest.raises(InvalidConfigError, match="banner"): | ||
| get_hconfig(Platform.CISCO_IOS, config_text) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import pytest | ||
|
|
||
| from hier_config import WorkflowRemediation, get_hconfig | ||
| from hier_config.exceptions import IncompatibleDriverError | ||
| from hier_config.models import Platform, TagRule | ||
|
|
||
|
|
||
|
|
@@ -50,7 +51,8 @@ def test_remediation_config_driver_mismatch() -> None: | |
| generated_config = get_hconfig(Platform.JUNIPER_JUNOS, "dummy_config") | ||
|
|
||
|
Comment on lines
51
to
52
|
||
| with pytest.raises( | ||
| ValueError, match=r"The running and generated configs must use the same driver." | ||
| IncompatibleDriverError, | ||
| match=r"The running and generated configs must use the same driver.", | ||
| ): | ||
| WorkflowRemediation(running_config, generated_config) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changelog entry describes a breaking behavior change (replacing
ValueErrorwith custom exceptions and reparentingDuplicateChildError). To match the existing changelog structure (e.g., other breaking renames listed under "Changed"), consider moving this bullet to the "### Changed" section or explicitly calling out that it’s breaking in the "Added" entry.