diff --git a/changes/75.misc.rst b/changes/75.misc.rst new file mode 100644 index 0000000..23e1496 --- /dev/null +++ b/changes/75.misc.rst @@ -0,0 +1 @@ +Cache ``to_camel_case``, speeding up building and serialising DIDL objects diff --git a/didl_lite/utils.py b/didl_lite/utils.py index 40afba0..9771027 100644 --- a/didl_lite/utils.py +++ b/didl_lite/utils.py @@ -1,5 +1,6 @@ """Utilities.""" +import functools import re NAMESPACES = { @@ -42,6 +43,10 @@ def split_namespace_tag(namespaced_tag: str) -> tuple[str | None, str]: return namespace, tag +# Cached: called for every attribute set on a DidlObject, with a small closed +# set of property names. Bounded, as from_xml also passes names read from +# documents received from other devices. +@functools.lru_cache(maxsize=256) def to_camel_case(name: str) -> str: """Get camel case of name.""" sub1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) diff --git a/tests/test_didl_lite.py b/tests/test_didl_lite.py index 8e9454d..743bced 100644 --- a/tests/test_didl_lite.py +++ b/tests/test_didl_lite.py @@ -3,7 +3,7 @@ import pytest from defusedxml import ElementTree as ET -from didl_lite import didl_lite +from didl_lite import didl_lite, utils NAMESPACES = { "didl_lite": "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/", @@ -793,3 +793,13 @@ def test_music_track_artist_and_genre(self) -> None: assert b"My Artist" in xml assert b"My Genre" in xml + + def test_to_camel_case_is_cached(self) -> None: + """Test to_camel_case is cached, and used cached through didl_lite.""" + assert hasattr(utils.to_camel_case, "cache_info") + # didl_lite imports the name; it must be the cached function too. + assert didl_lite.to_camel_case is utils.to_camel_case + + hits_before = utils.to_camel_case.cache_info().hits + didl_lite.MusicTrack(id="0", parent_id="0", title="Title", restricted="1") + assert utils.to_camel_case.cache_info().hits > hits_before