From 190e47ecdbbfbe4ca56726af26cfee66c060908c Mon Sep 17 00:00:00 2001 From: nvbn Date: Fri, 22 May 2015 17:07:01 +0300 Subject: [PATCH] #215 Use memoize decorator for caching --- tests/test_utils.py | 10 ++++++- thefuck/shells.py | 65 +++++++++++++++++++-------------------------- thefuck/utils.py | 17 ++++++++++++ 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 24d6b196..d180eb56 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,6 @@ import pytest from mock import Mock -from thefuck.utils import sudo_support, wrap_settings +from thefuck.utils import sudo_support, wrap_settings, memoize from thefuck.types import Settings from tests.utils import Command @@ -24,3 +24,11 @@ def test_sudo_support(return_value, command, called, result): fn = Mock(return_value=return_value, __name__='') assert sudo_support(fn)(Command(command), None) == result fn.assert_called_once_with(Command(called), None) + + +def test_memoize(): + fn = Mock(__name__='fn') + memoized = memoize(fn) + memoized() + memoized() + fn.assert_called_once_with() diff --git a/thefuck/shells.py b/thefuck/shells.py index 154311c8..6bf4bd7d 100644 --- a/thefuck/shells.py +++ b/thefuck/shells.py @@ -8,14 +8,13 @@ from subprocess import Popen, PIPE from time import time import os from psutil import Process -from .utils import DEVNULL +from .utils import DEVNULL, memoize class Generic(object): - _aliases = {} def get_aliases(self): - return self._aliases + return {} def _expand_aliases(self, command_script): aliases = self.get_aliases() @@ -63,16 +62,14 @@ class Bash(Generic): value = value[1:-1] return name, value + @memoize def get_aliases(self): - if not self._aliases: - proc = Popen('bash -ic alias', stdout=PIPE, stderr=DEVNULL, - shell=True) - self._aliases = dict( - self._parse_alias(alias) - for alias in proc.stdout.read().decode('utf-8').split('\n') - if alias and '=' in alias) - - return self._aliases + proc = Popen('bash -ic alias', stdout=PIPE, stderr=DEVNULL, + shell=True) + return dict( + self._parse_alias(alias) + for alias in proc.stdout.read().decode('utf-8').split('\n') + if alias and '=' in alias) def _get_history_file_name(self): return os.environ.get("HISTFILE", @@ -97,14 +94,12 @@ class Fish(Generic): " end\n" "end") + @memoize def get_aliases(self): - if not self._aliases: - proc = Popen('fish -ic functions', stdout=PIPE, stderr=DEVNULL, - shell=True) - functions = proc.stdout.read().decode('utf-8').strip().split('\n') - self._aliases = dict((function, function) for function in functions) - - return self._aliases + proc = Popen('fish -ic functions', stdout=PIPE, stderr=DEVNULL, + shell=True) + functions = proc.stdout.read().decode('utf-8').strip().split('\n') + return {function: function for function in functions} def _get_history_file_name(self): return os.path.expanduser('~/.config/fish/fish_history') @@ -126,16 +121,14 @@ class Zsh(Generic): value = value[1:-1] return name, value + @memoize def get_aliases(self): - if not self._aliases: - proc = Popen('zsh -ic alias', stdout=PIPE, stderr=DEVNULL, - shell=True) - self._aliases = dict( - self._parse_alias(alias) - for alias in proc.stdout.read().decode('utf-8').split('\n') - if alias and '=' in alias) - - return self._aliases + proc = Popen('zsh -ic alias', stdout=PIPE, stderr=DEVNULL, + shell=True) + return dict( + self._parse_alias(alias) + for alias in proc.stdout.read().decode('utf-8').split('\n') + if alias and '=' in alias) def _get_history_file_name(self): return os.environ.get("HISTFILE", @@ -153,16 +146,14 @@ class Tcsh(Generic): name, value = alias.split("\t", 1) return name, value + @memoize def get_aliases(self): - if not self._aliases: - proc = Popen('tcsh -ic alias', stdout=PIPE, stderr=DEVNULL, - shell=True) - self._aliases = dict( - self._parse_alias(alias) - for alias in proc.stdout.read().decode('utf-8').split('\n') - if alias and '\t' in alias) - - return self._aliases + proc = Popen('tcsh -ic alias', stdout=PIPE, stderr=DEVNULL, + shell=True) + return dict( + self._parse_alias(alias) + for alias in proc.stdout.read().decode('utf-8').split('\n') + if alias and '\t' in alias) def _get_history_file_name(self): return os.environ.get("HISTFILE", diff --git a/thefuck/utils.py b/thefuck/utils.py index 3247111d..8e5c9bda 100644 --- a/thefuck/utils.py +++ b/thefuck/utils.py @@ -1,5 +1,6 @@ from functools import wraps import os +import pickle import six from .types import Command @@ -62,3 +63,19 @@ def sudo_support(fn): else: return result return wrapper + + +def memoize(fn): + """Caches previous calls to the function.""" + memo = {} + + @wraps(fn) + def wrapper(*args, **kwargs): + key = pickle.dumps((args, kwargs)) + if key not in memo: + memo[key] = fn(*args, **kwargs) + + return memo[key] + + return wrapper +