From 85ec0e4738178f457eb172432d4fed123d947c71 Mon Sep 17 00:00:00 2001 From: Rongrong Date: Sun, 12 Nov 2023 23:25:59 +0800 Subject: [PATCH] [PR #7819/dfc3f899 backport][3.9] Skip filtering ``CookieJar`` when the jar is empty or all cookies have expired (#7822) **This is a backport of PR #7819 as merged into master (dfc3f899).** ## What do these changes do? The filtering itself and its preparation in `CookieJar.filter_cookies()` is expensive. Sometimes there are no cookies in the jar or all cookies have expired. Skip filtering and its preparation in this case. Because the empty check is much cheaper than `_do_expiration()`, I think it deserves to be duplicated before and after calling `_do_expiration()`. ```console $ python3.11 -m timeit -s 'from collections import defaultdict; d=defaultdict(foo="bar")' \ > 'if not d: pass' 50000000 loops, best of 5: 8.3 nsec per loop $ python3.11 -m timeit -s 'from collections import defaultdict; d=defaultdict()' \ > 'if not d: pass' 50000000 loops, best of 5: 8.74 nsec per loop $ python3.11 -m timeit -s 'from aiohttp import CookieJar; cj = CookieJar()' \ > 'cj._do_expiration()' 200000 loops, best of 5: 1.86 usec per loop ``` ## Are there changes in behavior for the user? No. ## Related issue number https://github.com/aio-libs/aiohttp/issues/7583#issuecomment-1806328904 ## Checklist - [x] I think the code is well written - [ ] Unit tests for the changes exist - [ ] Documentation reflects the changes - [x] If you provide code modification, please add yourself to `CONTRIBUTORS.txt` * The format is <Name> <Surname>. * Please keep alphabetical order, the file is sorted by names. - [x] Add a new news fragment into the `CHANGES` folder * name it `.` for example (588.bugfix) * if you don't have an `issue_id` change it to the pr id after creating the pr * ensure type is one of the following: * `.feature`: Signifying a new feature. * `.bugfix`: Signifying a bug fix. * `.doc`: Signifying a documentation improvement. * `.removal`: Signifying a deprecation or removal of public API. * `.misc`: A ticket has been closed, but it is not of interest to users. * Make sure to use full sentences with correct case and punctuation, for example: "Fix issue with non-ascii contents in doctest text files." --- CHANGES/7819.feature | 1 + CONTRIBUTORS.txt | 1 + aiohttp/cookiejar.py | 10 ++++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 CHANGES/7819.feature diff --git a/CHANGES/7819.feature b/CHANGES/7819.feature new file mode 100644 index 00000000000..6ff263abeb8 --- /dev/null +++ b/CHANGES/7819.feature @@ -0,0 +1 @@ +Skip filtering ``CookieJar`` when the jar is empty or all cookies have expired. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 557440d52aa..931d5c5b9aa 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -273,6 +273,7 @@ Required Field Robert Lu Robert Nikolich Roman Podoliaka +Rong Zhang Samir Akarioh Samuel Colvin Sean Hunt diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py index 86563274aa1..4fc3ec97e1f 100644 --- a/aiohttp/cookiejar.py +++ b/aiohttp/cookiejar.py @@ -236,11 +236,17 @@ def filter_cookies( self, request_url: URL = URL() ) -> Union["BaseCookie[str]", "SimpleCookie[str]"]: """Returns this jar's cookies filtered by their attributes.""" - self._do_expiration() - request_url = URL(request_url) filtered: Union["SimpleCookie[str]", "BaseCookie[str]"] = ( SimpleCookie() if self._quote_cookie else BaseCookie() ) + if not self._cookies: + # Skip do_expiration() if there are no cookies. + return filtered + self._do_expiration() + if not self._cookies: + # Skip rest of function if no non-expired cookies. + return filtered + request_url = URL(request_url) hostname = request_url.raw_host or "" request_origin = URL() with contextlib.suppress(ValueError):