From d3913dd3c4e5ccd1e433ef3df86b570f5c961a8b Mon Sep 17 00:00:00 2001 From: Stephen Rosen Date: Thu, 17 Sep 2026 15:26:27 -0500 Subject: [PATCH 1/2] Add a benchmark for "ordinary" scope parsing Using a very simple non-nested scope string, how long does it take to parse? --- tests/benchmark/test_scope_parser.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/benchmark/test_scope_parser.py b/tests/benchmark/test_scope_parser.py index fdcc18f63..c3cd62053 100644 --- a/tests/benchmark/test_scope_parser.py +++ b/tests/benchmark/test_scope_parser.py @@ -1,6 +1,6 @@ import pytest -from globus_sdk.scopes import ScopeParser +from globus_sdk.scopes import ScopeParser, TransferScopes def _make_deep_scope(depth): @@ -20,6 +20,11 @@ def _make_wide_scope(width): return big_scope +def test_ordinary_scope_parsing(benchmark): + scope_string = f"openid profile {TransferScopes.all}" + benchmark(ScopeParser.parse, scope_string) + + @pytest.mark.parametrize("depth", (10, 100, 1000, 2000, 3000, 4000, 5000)) def test_deep_scope_parsing(benchmark, depth): scope_string = _make_deep_scope(depth) From 47cc42bd6ae5d9ec65e0877935f166d93c5bd27a Mon Sep 17 00:00:00 2001 From: Stephen Rosen Date: Thu, 17 Sep 2026 15:31:36 -0500 Subject: [PATCH 2/2] Avoid unnecessary str slice (copy) in scope parse Using `data[1:]` (where `data` is a string) produces a new string, which necessitates a copy operation. For scope parsing, this is an avoidable cost, since the new string is only used for iteration under `enumerate()` anyway. Replace it with direct use of the enumerator, with an appropriate arithmetic tweak, and we can skip the copy. --- src/globus_sdk/scopes/_graph_parser.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/globus_sdk/scopes/_graph_parser.py b/src/globus_sdk/scopes/_graph_parser.py index 1718e0b73..0b7cf032e 100644 --- a/src/globus_sdk/scopes/_graph_parser.py +++ b/src/globus_sdk/scopes/_graph_parser.py @@ -237,8 +237,10 @@ def _peek_enumerate(data: str | list[str]) -> t.Iterator[tuple[int, str, str | N if not data: return - prev: str = data[0] - for idx, c in enumerate(data[1:]): + iterator = enumerate(data, start=-1) + prev: str + _, prev = next(iterator) + for idx, c in iterator: yield (idx, prev, c) prev = c