diff --git a/CHANGELOG.md b/CHANGELOG.md index 901d62a..09abb06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `TextStyle` type alias (`Literal["without_comments", "merged", "with_comments"]`) for + the `style` parameter on `HConfigChild.cisco_style_text()` and + `RemediationReporter.to_text()`, replacing the unconstrained `str` type (#189). + - Performance benchmarks for parsing, remediation, and iteration (#202). Skipped by default; run with `poetry run pytest -m benchmark -v -s`. diff --git a/hier_config/__init__.py b/hier_config/__init__.py index 9f78fe2..c0f2003 100644 --- a/hier_config/__init__.py +++ b/hier_config/__init__.py @@ -6,7 +6,7 @@ get_hconfig_from_dump, get_hconfig_view, ) -from .models import ChangeDetail, MatchRule, Platform, ReportSummary, TagRule +from .models import ChangeDetail, MatchRule, Platform, ReportSummary, TagRule, TextStyle from .reporting import RemediationReporter from .root import HConfig from .workflows import WorkflowRemediation @@ -20,6 +20,7 @@ "RemediationReporter", "ReportSummary", "TagRule", + "TextStyle", "WorkflowRemediation", "get_hconfig", "get_hconfig_driver", diff --git a/hier_config/child.py b/hier_config/child.py index 33080fd..d408e09 100644 --- a/hier_config/child.py +++ b/hier_config/child.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any from .base import HConfigBase -from .models import Instance, MatchRule, SetLikeOfStr +from .models import Instance, MatchRule, SetLikeOfStr, TextStyle if TYPE_CHECKING: from collections.abc import Iterable, Iterator @@ -181,7 +181,7 @@ def path(self) -> Iterator[str]: def cisco_style_text( self, - style: str = "without_comments", + style: TextStyle = "without_comments", tag: str | None = None, ) -> str: """Return a Cisco style formated line i.e. indentation_level + text ! comments.""" diff --git a/hier_config/models.py b/hier_config/models.py index c69f08a..cb5da59 100644 --- a/hier_config/models.py +++ b/hier_config/models.py @@ -1,8 +1,11 @@ from enum import Enum, auto +from typing import Literal from pydantic import BaseModel as PydanticBaseModel from pydantic import ConfigDict, NonNegativeInt, PositiveInt +TextStyle = Literal["without_comments", "merged", "with_comments"] + class BaseModel(PydanticBaseModel): """Pydantic.BaseModel with a safe config applied.""" diff --git a/hier_config/reporting.py b/hier_config/reporting.py index f2036a9..3b96096 100644 --- a/hier_config/reporting.py +++ b/hier_config/reporting.py @@ -14,7 +14,7 @@ from typing import Any from hier_config.child import HConfigChild -from hier_config.models import ChangeDetail, ReportSummary, TagRule +from hier_config.models import ChangeDetail, ReportSummary, TagRule, TextStyle from hier_config.root import HConfig @@ -637,7 +637,7 @@ def to_text( self, file_path: str | Path, *, - style: str = "merged", + style: TextStyle = "merged", include_tags: Iterable[str] = (), exclude_tags: Iterable[str] = (), ) -> None: diff --git a/tests/test_hier_config.py b/tests/test_hier_config.py index 90f7067..d5e6bc2 100644 --- a/tests/test_hier_config.py +++ b/tests/test_hier_config.py @@ -2187,3 +2187,16 @@ def test_children_extend() -> None: assert len(interface1.children) == 2 assert "description test" in interface1.children assert "ip address 192.0.2.1 255.255.255.0" in interface1.children + + +def test_cisco_style_text_literal_styles(platform_a: Platform) -> None: + """Verify cisco_style_text works with each valid TextStyle literal value (#189).""" + config = get_hconfig(platform_a) + child = config.add_child("interface Vlan2") + child.add_child("ip address 10.0.0.1 255.255.255.0") + + # Each valid style should produce a non-empty string without raising + for style in ("without_comments", "merged", "with_comments"): + result = child.cisco_style_text(style=style) + assert isinstance(result, str) + assert "interface Vlan2" in result