From 18470f345bb98162eabc0bc21b8dac5b6aaa824f Mon Sep 17 00:00:00 2001 From: Jose Paez Date: Sat, 17 Oct 2026 08:29:00 -0400 Subject: [PATCH 1/2] Cache to_camel_case DidlObject.__setattr__ resolves every attribute through to_camel_case, two re.sub calls each time, over a small closed set of property names. An LRU cache in front of it makes building objects ~5.8x faster and to_xml_string ~2.5x, with identical output. Bounded, since from_xml also passes names read from other devices' documents. --- didl_lite/utils.py | 5 +++++ tests/test_didl_lite.py | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) 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 From ac0a7b2f5e520bc0c7208e77b55f63183f786064 Mon Sep 17 00:00:00 2001 From: Jose Paez Date: Sat, 17 Oct 2026 08:30:01 -0400 Subject: [PATCH 2/2] Add changelog fragment --- changes/75.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/75.misc.rst 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