diff --git a/scrapling/parser.py b/scrapling/parser.py index 2b88af3..200cb43 100644 --- a/scrapling/parser.py +++ b/scrapling/parser.py @@ -671,7 +671,7 @@ class Selector(SelectorsGeneration): element_data = self.retrieve(identifier or selector) if element_data: elements = self.relocate(element_data, percentage) - if elements is not None and auto_save: + if elements and auto_save: self.save(elements[0], identifier or selector) return self.__handle_elements(elements) diff --git a/tests/parser/test_adaptive.py b/tests/parser/test_adaptive.py index 8d40f77..85881d1 100644 --- a/tests/parser/test_adaptive.py +++ b/tests/parser/test_adaptive.py @@ -56,6 +56,32 @@ class TestParserAdaptive: assert relocated[0].has_class("new-class") assert relocated[0].css(".new-description")[0].text == "Description 1" + def test_relocation_auto_save_no_match_above_threshold(self): + """Adaptive relocation with `auto_save=True` must not crash when no element + clears the `percentage` threshold (relocate() returns an empty list).""" + original_html = """ +
+
+

Widget

+

A widget

+
+
+ """ + # Unrelated structure so nothing can match a high threshold + changed_html = "totally unrelated content" + + old_page = Selector(original_html, url="example.com", adaptive=True) + new_page = Selector(changed_html, url="example.com", adaptive=True) + + old_page.css("#target", identifier="target", auto_save=True) + + # Before the fix this raised `IndexError: list index out of range` because the + # guard checked `elements is not None` but relocate() returns [] (never None). + result = new_page.css( + "#target", identifier="target", adaptive=True, auto_save=True, percentage=95 + ) + assert list(result) == [] + @pytest.mark.asyncio async def test_element_relocation_async(self): """Test relocating element after structure change in async mode"""