From 0bfffc2448aca6c9bc8f93109872502cdf12fc6c Mon Sep 17 00:00:00 2001 From: Jeremy Singer-Vine Date: Fri, 27 Oct 2023 10:52:25 -0400 Subject: [PATCH] Fix Page.get_textmap caching for extra_attrs=[...] ... by preconverting list kwargs to tuples. --- CHANGELOG.md | 6 ++++++ pdfplumber/page.py | 23 ++++++++++++++++++++--- tests/pdfs/extra-attrs-example.pdf | Bin 0 -> 15170 bytes tests/test_utils.py | 21 +++++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 tests/pdfs/extra-attrs-example.pdf diff --git a/CHANGELOG.md b/CHANGELOG.md index 55fea4db..14d38067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/). +## [0.10.4] - [Unreleased] + +### Fixed + +- Fix `Page.get_textmap` caching to allow for `extra_attrs=[...]`, by preconverting list kwargs to tuples. ([#1030](https://github.com/jsvine/pdfplumber/issues/1030)) + ## [0.10.3] - 2023-10-26 ### Added diff --git a/pdfplumber/page.py b/pdfplumber/page.py index c86a363c..fabfcd62 100644 --- a/pdfplumber/page.py +++ b/pdfplumber/page.py @@ -167,6 +167,23 @@ def paint_path(self, *args, **kwargs) -> None: # type: ignore self.tag_cur_item() +def textmap_cacher(func: Callable[..., TextMap]) -> Callable[..., TextMap]: + """ + Caches the kwargs to Page._get_textmap + and converts list kwargs (which would be unhashable) into tuples. + """ + + def new_func(**kwargs: Any) -> TextMap: + return lru_cache()(func)( + **{ + key: (tuple(value) if isinstance(value, list) else value) + for key, value in kwargs.items() + } + ) + + return new_func + + class Page(Container): cached_properties: List[str] = Container.cached_properties + ["_layout"] is_original: bool = True @@ -211,8 +228,8 @@ def __init__( ) ) - # https://rednafi.github.io/reflections/dont-wrap-instance-methods-with-functoolslru_cache-decorator-in-python.html - self.get_textmap = lru_cache()(self._get_textmap) + # https://rednafi.com/python/lru_cache_on_methods/ + self.get_textmap = textmap_cacher(self._get_textmap) @property def width(self) -> T_num: @@ -569,7 +586,7 @@ def __init__(self, parent_page: Page): self.page_obj = parent_page.page_obj self.page_number = parent_page.page_number self.flush_cache(Container.cached_properties) - self.get_textmap = lru_cache()(self._get_textmap) + self.get_textmap = textmap_cacher(self._get_textmap) def test_proposed_bbox(bbox: T_bbox, parent_bbox: T_bbox) -> None: diff --git a/tests/pdfs/extra-attrs-example.pdf b/tests/pdfs/extra-attrs-example.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1bd5d02af7ed4597d92ca4ea1f12b4d619db7edb GIT binary patch literal 15170 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