diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 706a578c..6d9169f0 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -36,11 +36,6 @@ repos:
rev: v0.3.1
hooks:
- id: absolufy-imports
- - repo: https://github.com/hakancelikdev/unimport
- rev: 1.1.0
- hooks:
- - id: unimport
- args: [--remove, --include-star-import, --ignore-init, --gitignore]
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.0
hooks:
diff --git a/fastkml/atom.py b/fastkml/atom.py
index 44efe964..d497d271 100644
--- a/fastkml/atom.py
+++ b/fastkml/atom.py
@@ -33,19 +33,15 @@
import logging
import re
+from typing import Any
+from typing import Dict
from typing import Optional
-from typing import Tuple
+from fastkml import config
from fastkml.base import _XMLObject
from fastkml.config import ATOMNS as NS
from fastkml.enums import Verbosity
-from fastkml.helpers import o_from_attr
-from fastkml.helpers import o_from_subelement_text
-from fastkml.helpers import o_int_from_attr
-from fastkml.helpers import o_to_attr
-from fastkml.helpers import o_to_subelement_text
from fastkml.types import Element
-from fastkml.types import KmlObjectMap
logger = logging.getLogger(__name__)
regex = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
@@ -68,61 +64,10 @@ class Link(_XMLObject):
__name__ = "link"
- kml_object_mapping: Tuple[KmlObjectMap, ...] = (
- {
- "kml_attr": "href",
- "obj_attr": "href",
- "from_kml": o_from_attr,
- "to_kml": o_to_attr,
- "required": True,
- "validator": None,
- },
- {
- "kml_attr": "rel",
- "obj_attr": "rel",
- "from_kml": o_from_attr,
- "to_kml": o_to_attr,
- "required": False,
- "validator": None,
- },
- {
- "kml_attr": "type",
- "obj_attr": "type",
- "from_kml": o_from_attr,
- "to_kml": o_to_attr,
- "required": False,
- "validator": None,
- },
- {
- "kml_attr": "hreflang",
- "obj_attr": "hreflang",
- "from_kml": o_from_attr,
- "to_kml": o_to_attr,
- "required": False,
- "validator": None,
- },
- {
- "kml_attr": "title",
- "obj_attr": "title",
- "from_kml": o_from_attr,
- "to_kml": o_to_attr,
- "required": False,
- "validator": None,
- },
- {
- "kml_attr": "length",
- "obj_attr": "length",
- "from_kml": o_int_from_attr,
- "to_kml": o_to_attr,
- "required": False,
- "validator": None,
- },
- )
-
- href = None
+ href: Optional[str]
# href is the URI of the referenced resource
- rel = None
+ rel: Optional[str]
# rel contains a single link relationship type.
# It can be a full URI, or one of the following predefined values
# (default=alternate):
@@ -134,21 +79,22 @@ class Link(_XMLObject):
# self: the feed itself.
# via: the source of the information provided in the entry.
- type = None
+ type: Optional[str]
# indicates the media type of the resource
- hreflang = None
+ hreflang: Optional[str]
# indicates the language of the referenced resource
- title = None
+ title: Optional[str]
# human readable information about the link
- length = None
+ length: Optional[int]
# the length of the resource, in bytes
def __init__(
self,
ns: Optional[str] = None,
+ name_spaces: Optional[Dict[str, str]] = None,
href: Optional[str] = None,
rel: Optional[str] = None,
type: Optional[str] = None,
@@ -156,7 +102,7 @@ def __init__(
title: Optional[str] = None,
length: Optional[int] = None,
) -> None:
- self.ns: str = NS if ns is None else ns
+ super().__init__(ns=ns, name_spaces=name_spaces)
self.href = href
self.rel = rel
self.type = type
@@ -177,15 +123,51 @@ def __repr__(self) -> str:
")"
)
- def from_element(self, element: Element) -> None:
- super().from_element(element)
-
def etree_element(
self,
precision: Optional[int] = None,
verbosity: Verbosity = Verbosity.normal,
) -> Element:
- return super().etree_element(precision=precision, verbosity=verbosity)
+ element = super().etree_element(precision=precision, verbosity=verbosity)
+ if self.href:
+ element.set("href", self.href)
+ else:
+ logger.warning("required attribute href missing")
+ if self.rel:
+ element.set("rel", self.rel)
+ if self.type:
+ element.set("type", self.type)
+ if self.hreflang:
+ element.set("hreflang", self.hreflang)
+ if self.title:
+ element.set("title", self.title)
+ if self.length:
+ element.set("length", str(self.length))
+ return element
+
+ @classmethod
+ def _get_kwargs(
+ cls,
+ *,
+ ns: str,
+ name_spaces: Optional[Dict[str, str]] = None,
+ element: Element,
+ strict: bool,
+ ) -> Dict[str, Any]:
+ kwargs = super()._get_kwargs(
+ ns=ns,
+ name_spaces=name_spaces,
+ element=element,
+ strict=strict,
+ )
+ kwargs["href"] = element.get("href")
+ kwargs["rel"] = element.get("rel")
+ kwargs["type"] = element.get("type")
+ kwargs["hreflang"] = element.get("hreflang")
+ kwargs["title"] = element.get("title")
+ length = element.get("length")
+ kwargs["length"] = int(length) if length else None
+ return kwargs
class _Person(_XMLObject):
@@ -196,50 +178,25 @@ class _Person(_XMLObject):
"""
__name__ = ""
- kml_object_mapping: Tuple[KmlObjectMap, ...] = (
- {
- "kml_attr": "name",
- "obj_attr": "name",
- "from_kml": o_from_subelement_text,
- "to_kml": o_to_subelement_text,
- "required": True,
- "validator": None,
- },
- {
- "kml_attr": "uri",
- "obj_attr": "uri",
- "from_kml": o_from_subelement_text,
- "to_kml": o_to_subelement_text,
- "required": False,
- "validator": None,
- },
- {
- "kml_attr": "email",
- "obj_attr": "email",
- "from_kml": o_from_subelement_text,
- "to_kml": o_to_subelement_text,
- "required": False,
- "validator": check_email,
- },
- )
-
- name: Optional[str] = None
+
+ name: Optional[str]
# conveys a human-readable name for the person.
- uri: Optional[str] = None
+ uri: Optional[str]
# contains a home page for the person.
- email: Optional[str] = None
+ email: Optional[str]
# contains an email address for the person.
def __init__(
self,
ns: Optional[str] = None,
+ name_spaces: Optional[Dict[str, str]] = None,
name: Optional[str] = None,
uri: Optional[str] = None,
email: Optional[str] = None,
) -> None:
- self.ns: str = NS if ns is None else ns
+ super().__init__(ns=ns, name_spaces=name_spaces)
self.name = name
self.uri = uri
self.email = email
@@ -259,10 +216,55 @@ def etree_element(
precision: Optional[int] = None,
verbosity: Verbosity = Verbosity.normal,
) -> Element:
- return super().etree_element(precision=precision, verbosity=verbosity)
-
- def from_element(self, element: Element) -> None:
- super().from_element(element)
+ self.__name__ = self.__class__.__name__.lower()
+ element = super().etree_element(precision=precision, verbosity=verbosity)
+ if self.name:
+ name = config.etree.SubElement( # type: ignore[attr-defined]
+ element,
+ f"{self.ns}name",
+ )
+ name.text = self.name
+ else:
+ logger.warning("No Name for person defined")
+ if self.uri:
+ uri = config.etree.SubElement( # type: ignore[attr-defined]
+ element,
+ f"{self.ns}uri",
+ )
+ uri.text = self.uri
+ if self.email and check_email(self.email):
+ email = config.etree.SubElement( # type: ignore[attr-defined]
+ element,
+ f"{self.ns}email",
+ )
+ email.text = self.email
+ return element
+
+ @classmethod
+ def _get_kwargs(
+ cls,
+ *,
+ ns: str,
+ name_spaces: Optional[Dict[str, str]] = None,
+ element: Element,
+ strict: bool,
+ ) -> Dict[str, Any]:
+ kwargs = super()._get_kwargs(
+ ns=ns,
+ name_spaces=name_spaces,
+ element=element,
+ strict=strict,
+ )
+ name = element.find(f"{ns}name")
+ if name is not None:
+ kwargs["name"] = name.text
+ uri = element.find(f"{ns}uri")
+ if uri is not None:
+ kwargs["uri"] = uri.text
+ email = element.find(f"{ns}email")
+ if email is not None:
+ kwargs["email"] = email.text
+ return kwargs
class Author(_Person):
@@ -285,4 +287,4 @@ class Contributor(_Person):
__name__ = "contributor"
-__all__ = ["Author", "Contributor", "Link"]
+__all__ = ["Author", "Contributor", "Link", "NS"]
diff --git a/fastkml/base.py b/fastkml/base.py
index 0181b0b7..49e519b9 100644
--- a/fastkml/base.py
+++ b/fastkml/base.py
@@ -19,15 +19,11 @@
from typing import Any
from typing import Dict
from typing import Optional
-from typing import Tuple
from typing import cast
from fastkml import config
from fastkml.enums import Verbosity
-from fastkml.helpers import o_from_attr
-from fastkml.helpers import o_to_attr
from fastkml.types import Element
-from fastkml.types import KmlObjectMap
logger = logging.getLogger(__name__)
@@ -39,7 +35,6 @@ class _XMLObject:
_node_name: str = ""
__name__ = ""
name_spaces: Dict[str, str]
- kml_object_mapping: Tuple[KmlObjectMap, ...] = ()
def __init__(
self,
@@ -72,8 +67,6 @@ def etree_element(
raise NotImplementedError(
msg,
)
- for mapping in self.kml_object_mapping:
- mapping["to_kml"](self, element, **mapping)
return element
def from_element(self, element: Element) -> None:
@@ -86,8 +79,6 @@ def from_element(self, element: Element) -> None:
if f"{self.ns}{self.__name__}" != element.tag:
msg = "Call of abstract base class, subclasses implement this!"
raise TypeError(msg)
- for mapping in self.kml_object_mapping:
- mapping["from_kml"](self, element, **mapping)
def from_string(self, xml_string: str) -> None:
"""
@@ -118,7 +109,9 @@ def to_string(
),
encoding="UTF-8",
pretty_print=prettyprint,
- ).decode("UTF-8"),
+ ).decode(
+ "UTF-8",
+ ),
)
except TypeError:
return cast(
@@ -126,7 +119,9 @@ def to_string(
config.etree.tostring( # type: ignore[attr-defined]
self.etree_element(),
encoding="UTF-8",
- ).decode("UTF-8"),
+ ).decode(
+ "UTF-8",
+ ),
)
@classmethod
@@ -216,24 +211,6 @@ class _BaseObject(_XMLObject):
id = None
target_id = None
- kml_object_mapping: Tuple[KmlObjectMap, ...] = (
- {
- "kml_attr": "id",
- "obj_attr": "id",
- "from_kml": o_from_attr,
- "to_kml": o_to_attr,
- "required": False,
- "validator": None,
- },
- {
- "kml_attr": "targetId",
- "obj_attr": "target_id",
- "from_kml": o_from_attr,
- "to_kml": o_to_attr,
- "required": False,
- "validator": None,
- },
- )
def __init__(
self,
@@ -268,11 +245,20 @@ def etree_element(
verbosity: Verbosity = Verbosity.normal,
) -> Element:
"""Return the KML Object as an Element."""
- return super().etree_element(precision=precision, verbosity=verbosity)
+ element = super().etree_element(precision=precision, verbosity=verbosity)
+ if self.id:
+ element.set("id", self.id)
+ if self.target_id:
+ element.set("targetId", self.target_id)
+ return element
def from_element(self, element: Element) -> None:
"""Load the KML Object from an Element."""
super().from_element(element)
+ if element.get("id"):
+ self.id = element.get("id")
+ if element.get("targetId"):
+ self.target_id = element.get("targetId")
@classmethod
def _get_id(cls, element: Element, strict: bool) -> str:
diff --git a/fastkml/geometry.py b/fastkml/geometry.py
index df8ae604..ef251ffb 100644
--- a/fastkml/geometry.py
+++ b/fastkml/geometry.py
@@ -380,7 +380,9 @@ def _get_geometry(
error = config.etree.tostring( # type: ignore[attr-defined]
element,
encoding="UTF-8",
- ).decode("UTF-8")
+ ).decode(
+ "UTF-8",
+ )
msg = f"Invalid coordinates in {error}"
raise KMLParseError(msg) from e
@@ -436,7 +438,9 @@ def _get_geometry(
error = config.etree.tostring( # type: ignore[attr-defined]
element,
encoding="UTF-8",
- ).decode("UTF-8")
+ ).decode(
+ "UTF-8",
+ )
msg = f"Invalid coordinates in {error}"
raise KMLParseError(msg) from e
@@ -480,7 +484,9 @@ def _get_geometry(
error = config.etree.tostring( # type: ignore[attr-defined]
element,
encoding="UTF-8",
- ).decode("UTF-8")
+ ).decode(
+ "UTF-8",
+ )
msg = f"Invalid coordinates in {error}"
raise KMLParseError(msg) from e
@@ -554,7 +560,9 @@ def _get_geometry(cls, *, ns: str, element: Element, strict: bool) -> geo.Polygo
error = config.etree.tostring( # type: ignore[attr-defined]
element,
encoding="UTF-8",
- ).decode("UTF-8")
+ ).decode(
+ "UTF-8",
+ )
msg = f"Missing outerBoundaryIs in {error}"
raise KMLParseError(msg)
outer_ring = outer_boundary.find(f"{ns}LinearRing")
@@ -562,7 +570,9 @@ def _get_geometry(cls, *, ns: str, element: Element, strict: bool) -> geo.Polygo
error = config.etree.tostring( # type: ignore[attr-defined]
element,
encoding="UTF-8",
- ).decode("UTF-8")
+ ).decode(
+ "UTF-8",
+ )
msg = f"Missing LinearRing in {error}"
raise KMLParseError(msg)
exterior = LinearRing._get_geometry(ns=ns, element=outer_ring, strict=strict)
@@ -573,7 +583,9 @@ def _get_geometry(cls, *, ns: str, element: Element, strict: bool) -> geo.Polygo
error = config.etree.tostring( # type: ignore[attr-defined]
element,
encoding="UTF-8",
- ).decode("UTF-8")
+ ).decode(
+ "UTF-8",
+ )
msg = f"Missing LinearRing in {error}"
raise KMLParseError(msg)
interiors.append(
diff --git a/fastkml/helpers.py b/fastkml/helpers.py
deleted file mode 100644
index 5c678a00..00000000
--- a/fastkml/helpers.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Copyright (C) 2020 Christian Ledermann
-#
-# This library is free software; you can redistribute it and/or modify it under
-# the terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation; either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# This library is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this library; if not, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-"""Helper functions and classes."""
-import logging
-from typing import Any
-from typing import Callable
-from typing import Optional
-
-from fastkml import config
-from fastkml.types import Element
-
-__all__ = [
- "o_from_attr",
- "o_from_subelement_text",
- "o_int_from_attr",
- "o_to_attr",
- "o_to_subelement_text",
-]
-
-logger = logging.getLogger(__name__)
-
-
-def o_to_attr(
- obj: object,
- element: Element,
- kml_attr: str,
- obj_attr: str,
- required: bool,
- **kwargs: Any,
-) -> None:
- """Set an attribute on an KML Element from an object attribute."""
- attribute = getattr(obj, obj_attr)
- if attribute:
- element.set(kml_attr, str(attribute))
- elif required:
- logger.warning(
- "Required attribute '%s' for '%s' missing.",
- obj_attr,
- obj.__class__.__name__,
- )
-
-
-def o_from_attr(
- obj: object,
- element: Element,
- kml_attr: str,
- obj_attr: str,
- required: bool,
- **kwargs: Any,
-) -> None:
- """Set an attribute on self from an KML attribute."""
- attribute = element.get(kml_attr)
- if attribute:
- setattr(obj, obj_attr, attribute)
- elif required:
- logger.warning(
- "Required attribute '%s' for '%s' missing.",
- kml_attr,
- obj.__class__.__name__,
- )
-
-
-def o_int_from_attr(
- obj: object,
- element: Element,
- kml_attr: str,
- obj_attr: str,
- required: bool,
- **kwargs: Any,
-) -> None:
- """Set an attribute on self from an KML attribute."""
- try:
- attribute = int(element.get(kml_attr))
- except (ValueError, TypeError):
- attribute = None
- if attribute is not None:
- setattr(obj, obj_attr, attribute)
- elif required:
- logger.warning(
- "Required attribute '%s' for '%s' missing.",
- kml_attr,
- obj.__class__.__name__,
- )
-
-
-def o_from_subelement_text(
- obj: object,
- element: Element,
- kml_attr: str,
- obj_attr: str,
- required: bool,
- validator: Optional[Callable[..., bool]] = None,
- **kwargs: Any,
-) -> None:
- """Set an attribute on self from the text of a SubElement."""
- elem = element.find(f"{obj.ns}{kml_attr}") # type: ignore[attr-defined]
- if elem is not None:
- if validator is not None and not validator(elem.text):
- logger.warning(
- "Invalid value for attribute '%s' for '%s'",
- kml_attr,
- obj.__class__.__name__,
- )
- else:
- setattr(obj, obj_attr, elem.text)
- elif required:
- logger.warning(
- "Required attribute '%s' for '%s' missing.",
- kml_attr,
- obj.__class__.__name__,
- )
-
-
-def o_to_subelement_text(
- obj: object,
- element: Element,
- kml_attr: str,
- obj_attr: str,
- required: bool,
- validator: Optional[Callable[..., bool]] = None,
- **kwargs: Any,
-) -> None:
- """Set the text of a SubElement from an object attribute."""
- attribute = getattr(obj, obj_attr)
- if attribute:
- if validator is not None and not validator(attribute):
- logger.warning(
- "Invalid value for attribute '%s' for '%s'",
- obj_attr,
- obj.__class__.__name__,
- )
- else:
- elem = config.etree.SubElement( # type: ignore[attr-defined]
- element,
- f"{obj.ns}{kml_attr}", # type: ignore[attr-defined]
- )
- elem.text = str(attribute)
- elif required:
- logger.warning(
- "Required attribute '%s' for '%s' missing.",
- obj_attr,
- obj.__class__.__name__,
- )
diff --git a/fastkml/kml.py b/fastkml/kml.py
index b8b958ef..2d3b02b9 100644
--- a/fastkml/kml.py
+++ b/fastkml/kml.py
@@ -272,7 +272,7 @@ def author(self, name) -> None:
self._atom_author = name
elif isinstance(name, str):
if self._atom_author is None:
- self._atom_author = atom.Author(name=name)
+ self._atom_author = atom.Author(ns=config.ATOMNS, name=name)
else:
self._atom_author.name = name
elif name is None:
@@ -489,14 +489,20 @@ def from_element(self, element: Element, strict: bool = False) -> None:
)
atom_link = element.find(f"{atom.NS}link")
if atom_link is not None:
- s = atom.Link()
- s.from_element(atom_link)
- self._atom_link = s
+ self._atom_link = atom.Link.class_from_element(
+ ns=atom.NS,
+ name_spaces=self.name_spaces,
+ element=atom_link,
+ strict=strict,
+ )
atom_author = element.find(f"{atom.NS}author")
if atom_author is not None:
- s = atom.Author()
- s.from_element(atom_author)
- self._atom_author = s
+ self._atom_author = atom.Author.class_from_element(
+ ns=atom.NS,
+ name_spaces=self.name_spaces,
+ element=atom_author,
+ strict=strict,
+ )
extended_data = element.find(f"{self.ns}ExtendedData")
if extended_data is not None:
self.extended_data = ExtendedData.class_from_element(
diff --git a/fastkml/types.py b/fastkml/types.py
index 16288e03..0088ecf2 100644
--- a/fastkml/types.py
+++ b/fastkml/types.py
@@ -15,14 +15,12 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"""Types for fastkml."""
-from typing import Callable
from typing import Iterable
from typing import Optional
from typing_extensions import Protocol
-from typing_extensions import TypedDict
-__all__ = ["Element", "KmlObjectMap"]
+__all__ = ["Element"]
class Element(Protocol):
@@ -48,14 +46,3 @@ def append(self, element: "Element") -> None:
def remove(self, element: "Element") -> None:
"""Remove an element from the current element."""
-
-
-class KmlObjectMap(TypedDict):
- """TypedDict for KmlObjectMap."""
-
- kml_attr: str
- obj_attr: str
- required: bool
- from_kml: Callable[..., None]
- to_kml: Callable[..., None]
- validator: Optional[Callable[..., bool]]
diff --git a/tests/atom_test.py b/tests/atom_test.py
index 533448a1..2b9955b9 100644
--- a/tests/atom_test.py
+++ b/tests/atom_test.py
@@ -34,6 +34,7 @@ def test_atom_link_ns(self) -> None:
def test_atom_link(self) -> None:
link = atom.Link(
+ ns="{http://www.w3.org/2005/Atom}",
href="#here",
rel="alternate",
type="text/html",
@@ -53,8 +54,7 @@ def test_atom_link(self) -> None:
assert 'length="3456"' in serialized
def test_atom_link_read(self) -> None:
- link = atom.Link()
- link.from_string(
+ link = atom.Link.class_from_string(
'',
@@ -67,8 +67,7 @@ def test_atom_link_read(self) -> None:
assert link.length == 3456
def test_atom_link_read_no_href(self) -> None:
- link = atom.Link()
- link.from_string(
+ link = atom.Link.class_from_string(
'',
@@ -82,6 +81,7 @@ def test_atom_person_ns(self) -> None:
def test_atom_author(self) -> None:
a = atom.Author(
+ ns="{http://www.w3.org/2005/Atom}",
name="Nobody",
uri="http://localhost",
email="cl@donotreply.com",
@@ -95,11 +95,11 @@ def test_atom_author(self) -> None:
assert "" in serialized
def test_atom_author_read(self) -> None:
- a = atom.Author()
- a.from_string(
+ a = atom.Author.class_from_string(
''
"Nobodyhttp://localhost"
"cl@donotreply.com",
+ ns="{http://www.w3.org/2005/Atom}",
)
assert a.name == "Nobody"
@@ -107,11 +107,11 @@ def test_atom_author_read(self) -> None:
assert a.email == "cl@donotreply.com"
def test_atom_contributor_read_no_name(self) -> None:
- a = atom.Contributor()
- a.from_string(
+ a = atom.Contributor.class_from_string(
''
"http://localhost"
"cl@donotreply.com",
+ ns="{http://www.w3.org/2005/Atom}",
)
assert a.name is None
@@ -130,8 +130,7 @@ def test_author_roundtrip(self) -> None:
a.email = "christian@gmail.com"
a.email = "christian"
assert "email>" not in str(a.to_string())
- a2 = atom.Author()
- a2.from_string(a.to_string())
+ a2 = atom.Author.class_from_string(a.to_string())
assert a.to_string() == a2.to_string()
def test_link_roundtrip(self) -> None:
@@ -140,8 +139,7 @@ def test_link_roundtrip(self) -> None:
link.type = "text/html"
link.hreflang = "en"
link.length = 4096
- l2 = atom.Link()
- l2.from_string(link.to_string())
+ l2 = atom.Link.class_from_string(link.to_string())
assert link.to_string() == l2.to_string()
diff --git a/tests/oldunit_test.py b/tests/oldunit_test.py
index 02b8f5fa..d0fca0c0 100644
--- a/tests/oldunit_test.py
+++ b/tests/oldunit_test.py
@@ -195,6 +195,7 @@ def test_author(self) -> None:
d.author = "Christian Ledermann"
assert "Christian Ledermann" in str(d.to_string())
a = atom.Author(
+ ns="{http://www.w3.org/2005/Atom}",
name="Nobody",
uri="http://localhost",
email="cl@donotreply.com",
@@ -214,7 +215,7 @@ def test_link(self) -> None:
d = kml.Document()
d.link = "http://localhost"
assert "http://localhost" in str(d.to_string())
- d.link = atom.Link(href="#here")
+ d.link = atom.Link(ns=config.ATOMNS, href="#here")
assert "#here" in str(d.to_string())
# pytest.raises(TypeError, d.link, object)
d2 = kml.Document()