Skip to content

Instantly share code, notes, and snippets.

@SimplyAhmazing
Last active September 25, 2016 16:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SimplyAhmazing/0007764f0acf39b1bf39277a70718ea7 to your computer and use it in GitHub Desktop.
Save SimplyAhmazing/0007764f0acf39b1bf39277a70718ea7 to your computer and use it in GitHub Desktop.

Below is a class decorator did_you_mean that will, when applied to a class, catch undefined methods on that class and return y ou a recommendation of methods to call instead.

from fuzzywuzzy.process import extractOne


def get_class_methods(cls_instance):
    return [name for name in dir(cls_instance) if not name.startswith('__')]


def get_recommendation(query, search_space, confidence_cutoff):
    match = extractOne(query, search_space)
    if match:
        recommendation, calculated_confidence = match
        return (
            recommendation
            if calculated_confidence >= confidence_cutoff
            else None
        )


def did_you_mean(confidence):
    def _did_you_mean(self, name):
        def wrapper(*args, **kwargs):
            msg = 'The method "{}" does not exist...'.format(name)
            cls_methods = get_class_methods(self)
            recommendation = get_recommendation(name, cls_methods, confidence)
            if recommendation:
                msg += 'Did you mean to call: **{}()**?'.format(recommendation)
            else:
                msg += 'Instead try one of these: {}()'.format('(), '.join(cls_methods))
            print(msg)
            return self
        return wrapper

    def class_wrapper(cls):
        setattr(cls, '__getattr__', _did_you_mean)
        return cls
    return class_wrapper


@did_you_mean(confidence=50)
class Pipette(object):
    def foo(self):
        print('foo was called')
        return self

    def bar(self):
        print('bar was called')
        return self

    def baz(self):
        print('baz was called')
        return self


if __name__ == '__main__':
    p200 = Pipette()

    # This should recommend that you call 'bar' instead of 'bat'
    p200.foo().bar().baz().bat()

    # This should recommend all the methods in the calss since 'zzzz' is not
    # close to any of the methods in the Pipette class withing the desired ratio
    # but then continues to call other methods...
    p200.zzzz().foo().bar()

If you run the above script in __main__ without applying did_you_mean you get the standard python AttributeError:

$ python did_you_mean.py                            
foo was called
bar was called
baz was called
Traceback (most recent call last):
  File "did_you_mean.py", line 38, in <module>
    p200.foo().bar().baz().bat()
AttributeError: 'Pipette' object has no attribute 'bat'

After applying did_you_mean:

python did_you_mean.py                            
foo was called
bar was called
baz was called
The method "bat" does not exist...Did you mean to call: **bar()**?
The method "zzzz" does not exist...Instead try one of these: bar(), baz(), foo()
foo was called
bar was called

To replicate use a python env with python 3.3.2 with fuzzywuzzy and python-Levenshtein packages installed...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment