From 6993a1ea4640e89928414a4c59d5cec3d2a9e64a Mon Sep 17 00:00:00 2001 From: Rodrigo Parra Date: Fri, 2 Nov 2018 06:32:01 -0300 Subject: [PATCH] Replace memoize decorator by functools.lru_cache --- responder/routes.py | 24 +++++++++++------------- tests/test_routes.py | 23 ++++++++++++----------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/responder/routes.py b/responder/routes.py index 79b6a01..f81e574 100644 --- a/responder/routes.py +++ b/responder/routes.py @@ -1,17 +1,8 @@ import re +import functools from parse import parse -def memoize(f): - def helper(self, s): - memoize_key = f"{f.__name__}:{s}" - if memoize_key not in self._memo: - self._memo[memoize_key] = f(self, s) - return self._memo[memoize_key] - - return helper - - class Route: _param_pattern = re.compile(r"{([^{}]*)}") @@ -20,7 +11,6 @@ class Route: self.endpoint = endpoint self.uses_websocket = websocket self.before_request = before_request - self._memo = {} def __repr__(self): return f"" @@ -45,7 +35,7 @@ class Route: def has_parameters(self): return bool(self._param_pattern.search(self.route)) - @memoize + @functools.lru_cache(maxsize=None) def does_match(self, s): if s == self.route: return True @@ -53,7 +43,7 @@ class Route: named = self.incoming_matches(s) return bool(len(named)) - @memoize + @functools.lru_cache(maxsize=None) def incoming_matches(self, s): results = parse(self.route, s) return results.named if results else {} @@ -75,3 +65,11 @@ class Route: code = hasattr(self.endpoint, "__code__") kwdefaults = hasattr(self.endpoint, "__kwdefaults__") return all((callable(self.endpoint), code, kwdefaults)) + + def __hash__(self): + return ( + hash(self.route) + ^ hash(self.endpoint) + ^ hash(self.uses_websocket) + ^ hash(self.before_request) + ) diff --git a/tests/test_routes.py b/tests/test_routes.py index 0ae2606..c480c6d 100644 --- a/tests/test_routes.py +++ b/tests/test_routes.py @@ -2,6 +2,10 @@ import pytest from responder import routes +def setup_function(function): + routes.Route.incoming_matches.cache_clear() + + @pytest.mark.parametrize( "route, expected", [ @@ -36,26 +40,23 @@ def test_incoming_matches(): assert r.incoming_matches("/hello") == {"greetings": "hello"} assert r.incoming_matches("/foo") == {"greetings": "foo"} - assert r._memo == { - "incoming_matches:/hello": {"greetings": "hello"}, - "incoming_matches:/foo": {"greetings": "foo"}, - } - # Test Route with two params r = routes.Route("/{greetings}/{name}", "test_endpoint") assert r.incoming_matches("/hi/john") == {"greetings": "hi", "name": "john"} assert r.incoming_matches("/hello/jane") == {"greetings": "hello", "name": "jane"} # Test Route with no param - assert r._memo == { - "incoming_matches:/hi/john": {"greetings": "hi", "name": "john"}, - "incoming_matches:/hello/jane": {"greetings": "hello", "name": "jane"}, - } - r = routes.Route("/hello", "test_endpoint") assert r.incoming_matches("/hello") == {} assert r.incoming_matches("/bye") == {} - assert r._memo == {"incoming_matches:/hello": {}, "incoming_matches:/bye": {}} + + +def test_incoming_matches_cache(): + r = routes.Route("/hello", "test_endpoint") + r.incoming_matches("/hello") + assert r.incoming_matches.cache_info().hits == 0 + r.incoming_matches("/hello") + assert r.incoming_matches.cache_info().hits == 1 def test_incoming_matches_with_concrete_path_no_match():