From 65c896b1f085df9ba4e606dbaa7f40ba115cb3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 15 Mar 2019 13:37:17 +0100 Subject: [PATCH] Do not fail on importing string tools if missing python-Levenshtein python-Levenshtein is an optional dependency so trytond.tools.string_ should not fail on import. issue8040 --- trytond/ir/model.py | 5 +- trytond/tools/string_.py | 118 ++++++++++++++++++++------------------- 2 files changed, 63 insertions(+), 60 deletions(-) diff --git a/trytond/ir/model.py b/trytond/ir/model.py index f0213b4e6c..89d5fe7299 100644 --- a/trytond/ir/model.py +++ b/trytond/ir/model.py @@ -24,10 +24,7 @@ from ..pyson import Bool, Eval, PYSONDecoder from ..rpc import RPC from ..protocols.jsonrpc import JSONDecoder, JSONEncoder from ..tools import is_instance_method, cursor_dict, grouped_slice -try: - from ..tools.string import StringMatcher -except ImportError: - from difflib import SequenceMatcher as StringMatcher +from ..tools.string_ import StringMatcher __all__ = [ 'Model', 'ModelField', 'ModelAccess', 'ModelFieldAccess', 'ModelButton', diff --git a/trytond/tools/string_.py b/trytond/tools/string_.py index 2e9080a1ff..8d78176cf1 100644 --- a/trytond/tools/string_.py +++ b/trytond/tools/string_.py @@ -3,76 +3,82 @@ # Code come from python-Levenshtein -from Levenshtein import opcodes, editops, matching_blocks, ratio, distance +__all__ = ['StringMatcher', 'StringPartitioned'] + from warnings import warn +try: + from Levenshtein import opcodes, editops, matching_blocks, ratio, distance + class StringMatcher: + """A SequenceMatcher-like class built on the top of Levenshtein""" -class StringMatcher: - """A SequenceMatcher-like class built on the top of Levenshtein""" + def _reset_cache(self): + self._ratio = self._distance = None + self._opcodes = self._editops = self._matching_blocks = None - def _reset_cache(self): - self._ratio = self._distance = None - self._opcodes = self._editops = self._matching_blocks = None + def __init__(self, isjunk=None, seq1='', seq2=''): + if isjunk: + warn("isjunk not NOT implemented, it will be ignored") + self._str1, self._str2 = seq1, seq2 + self._reset_cache() - def __init__(self, isjunk=None, seq1='', seq2=''): - if isjunk: - warn("isjunk not NOT implemented, it will be ignored") - self._str1, self._str2 = seq1, seq2 - self._reset_cache() + def set_seqs(self, seq1, seq2): + self._str1, self._str2 = seq1, seq2 + self._reset_cache() - def set_seqs(self, seq1, seq2): - self._str1, self._str2 = seq1, seq2 - self._reset_cache() + def set_seq1(self, seq1): + self._str1 = seq1 + self._reset_cache() - def set_seq1(self, seq1): - self._str1 = seq1 - self._reset_cache() + def set_seq2(self, seq2): + self._str2 = seq2 + self._reset_cache() - def set_seq2(self, seq2): - self._str2 = seq2 - self._reset_cache() + def get_opcodes(self): + if not self._opcodes: + if self._editops: + self._opcodes = opcodes( + self._editops, self._str1, self._str2) + else: + self._opcodes = opcodes(self._str1, self._str2) + return self._opcodes - def get_opcodes(self): - if not self._opcodes: - if self._editops: - self._opcodes = opcodes(self._editops, self._str1, self._str2) - else: - self._opcodes = opcodes(self._str1, self._str2) - return self._opcodes + def get_editops(self): + if not self._editops: + if self._opcodes: + self._editops = editops( + self._opcodes, self._str1, self._str2) + else: + self._editops = editops(self._str1, self._str2) + return self._editops - def get_editops(self): - if not self._editops: - if self._opcodes: - self._editops = editops(self._opcodes, self._str1, self._str2) - else: - self._editops = editops(self._str1, self._str2) - return self._editops + def get_matching_blocks(self): + if not self._matching_blocks: + self._matching_blocks = matching_blocks(self.get_opcodes(), + self._str1, self._str2) + return self._matching_blocks - def get_matching_blocks(self): - if not self._matching_blocks: - self._matching_blocks = matching_blocks(self.get_opcodes(), - self._str1, self._str2) - return self._matching_blocks + def ratio(self): + if not self._ratio: + self._ratio = ratio(self._str1, self._str2) + return self._ratio - def ratio(self): - if not self._ratio: - self._ratio = ratio(self._str1, self._str2) - return self._ratio + def quick_ratio(self): + # This is usually quick enough :o) + if not self._ratio: + self._ratio = ratio(self._str1, self._str2) + return self._ratio - def quick_ratio(self): - # This is usually quick enough :o) - if not self._ratio: - self._ratio = ratio(self._str1, self._str2) - return self._ratio + def real_quick_ratio(self): + len1, len2 = len(self._str1), len(self._str2) + return 2.0 * min(len1, len2) / (len1 + len2) - def real_quick_ratio(self): - len1, len2 = len(self._str1), len(self._str2) - return 2.0 * min(len1, len2) / (len1 + len2) - - def distance(self): - if not self._distance: - self._distance = distance(self._str1, self._str2) - return self._distance + def distance(self): + if not self._distance: + self._distance = distance(self._str1, self._str2) + return self._distance +except ImportError: + from difflib import SequenceMatcher as StringMatcher class StringPartitioned(str):