diff --git a/fastkml/features.py b/fastkml/features.py index 49135eec..721f27b4 100644 --- a/fastkml/features.py +++ b/fastkml/features.py @@ -5,6 +5,7 @@ """ import logging +from dataclasses import dataclass from typing import Any from typing import Dict from typing import Iterator @@ -50,6 +51,12 @@ ] +@dataclass(frozen=True) +class Snippet: + text: str + max_lines: Optional[int] = None + + class _Feature(TimeMixin, _BaseObject): """ abstract element; do not create @@ -95,7 +102,7 @@ class _Feature(TimeMixin, _BaseObject): # A string value representing a telephone number. # This element is used by Google Maps Mobile only. - _snippet: Optional[Union[str, Dict[str, Any]]] + _snippet: Optional[Snippet] # _snippet is either a tuple of a string Snippet.text and an integer # Snippet.maxLines or a string # @@ -163,7 +170,7 @@ def __init__( atom_author: Optional[atom.Author] = None, address: Optional[str] = None, phone_number: Optional[str] = None, - snippet: Optional[Union[str, Dict[str, Any]]] = None, + snippet: Optional[Snippet] = None, description: Optional[str] = None, view: Optional[Union[Camera, LookAt]] = None, times: Optional[Union[TimeSpan, TimeStamp]] = None, @@ -274,46 +281,12 @@ def styles(self) -> Iterator[Union[Style, StyleMap]]: raise TypeError @property - def snippet(self) -> Optional[Dict[str, Any]]: - if not self._snippet: - return None - if isinstance(self._snippet, dict): - text = self._snippet.get("text") - if text: - assert isinstance(text, str) - max_lines = self._snippet.get("maxLines", None) - if max_lines is None: - return {"text": text} - elif int(max_lines) > 0: - # if maxLines <=0 ignore it - return {"text": text, "maxLines": max_lines} - return None - return None - elif isinstance(self._snippet, str): - return self._snippet - else: - msg = "Snippet must be dict of {'text':t, 'maxLines':i} or string" - raise ValueError( - msg, - ) + def snippet(self) -> Optional[Snippet]: + return self._snippet @snippet.setter - def snippet(self, snip=None) -> None: - self._snippet = {} - if isinstance(snip, dict): - self._snippet["text"] = snip.get("text") - max_lines = snip.get("maxLines") - if max_lines is not None: - self._snippet["maxLines"] = int(snip["maxLines"]) - elif isinstance(snip, str): - self._snippet["text"] = snip - elif snip is None: - self._snippet = None - else: - msg = "Snippet must be dict of {'text':t, 'maxLines':i} or string" - raise ValueError( - msg, - ) + def snippet(self, snippet: Optional[Snippet]) -> None: + self._snippet = snippet @property def address(self) -> Optional[str]: @@ -357,13 +330,9 @@ def etree_element( element.append(style.etree_element()) if self.snippet: snippet = config.etree.SubElement(element, f"{self.ns}Snippet") - if isinstance(self.snippet, str): - snippet.text = self.snippet - else: - assert isinstance(self.snippet["text"], str) - snippet.text = self.snippet["text"] - if self.snippet.get("maxLines"): - snippet.set("maxLines", str(self.snippet["maxLines"])) + snippet.text = self.snippet.text + if self.snippet.max_lines: + snippet.set("maxLines", str(self.snippet.max_lines)) elif self._times is not None: element.append(self._times.etree_element()) if self._atom_link is not None: @@ -422,10 +391,11 @@ def from_element(self, element: Element, strict: bool = False) -> None: ) snippet = element.find(f"{self.ns}Snippet") if snippet is not None: - _snippet = {"text": snippet.text} - if snippet.get("maxLines"): - _snippet["maxLines"] = int(snippet.get("maxLines")) - self.snippet = _snippet + max_lines = snippet.get("maxLines") + if max_lines is not None: + self.snippet = Snippet(text=snippet.text, max_lines=int(max_lines)) + else: + self.snippet = Snippet(text=snippet.text) timespan = element.find(f"{self.ns}TimeSpan") if timespan is not None: self._timespan = TimeSpan.class_from_element( diff --git a/fastkml/types.py b/fastkml/types.py index 0088ecf2..2c0b7cfd 100644 --- a/fastkml/types.py +++ b/fastkml/types.py @@ -29,7 +29,7 @@ class Element(Protocol): tag: str text: str - def set(self, tag: str, value: str) -> None: + def set(self, tag: str, value: str) -> None: # noqa: A003 """Set the value of the tag.""" def get(self, tag: str) -> str: diff --git a/tests/oldunit_test.py b/tests/oldunit_test.py index d3f33b22..05d512c3 100644 --- a/tests/oldunit_test.py +++ b/tests/oldunit_test.py @@ -23,6 +23,7 @@ from fastkml import atom from fastkml import base from fastkml import config +from fastkml import features from fastkml import kml from fastkml import styles from fastkml.enums import ColorMode @@ -534,17 +535,18 @@ def test_snippet(self) -> None: k = kml.KML() k.from_string(doc) - assert next(iter(k.features())).snippet["text"] == "Short Desc" - assert next(iter(k.features())).snippet["maxLines"] == 2 - next(iter(k.features()))._snippet["maxLines"] = 3 - assert next(iter(k.features())).snippet["maxLines"] == 3 + assert next(iter(k.features())).snippet.text == "Short Desc" + assert next(iter(k.features())).snippet.max_lines == 2 + next(iter(k.features()))._snippet = features.Snippet( + text="Another Snippet", max_lines=3 + ) assert 'maxLines="3"' in k.to_string() - next(iter(k.features())).snippet = {"text": "Annother Snippet"} + next(iter(k.features())).snippet = features.Snippet(text="Another Snippet") assert "maxLines" not in k.to_string() - assert "Annother Snippet" in k.to_string() - next(iter(k.features())).snippet = "Diffrent Snippet" + assert "Another Snippet" in k.to_string() + next(iter(k.features())).snippet = features.Snippet("Different Snippet") assert "maxLines" not in k.to_string() - assert "Diffrent Snippet" in k.to_string() + assert "Different Snippet" in k.to_string() def test_from_wrong_string(self) -> None: doc = kml.KML()