0

So I'm working with an an API that returns a JSON after I send a query to it. I wrote a wrapper class around that JSON response so I could handle any change in the JSON in one place. It also helps that I can access the values easily now.

Here is the wrapper I wrote:

class WitResponse:
    def __init__(self, json_string):
        self.json_string = json_string
        self.wit_response = json.loads(json_string)

    @property
    def intent(self):
        return self.wit_response["outcomes"][0]["intent"]

    @property
    def confidence(self):
        return self.wit_response["outcomes"][0]["confidence"]

    @property
    def text(self):
        return self.wit_response["_text"] 

    @property
    def entites(self):
        return self.wit_response["outcomes"][0]["entites"]

    @property
    def msg_id(self):
        return self.wit_response["msg_id"] 

    @property
    def response(self):
        return self.json_string.translate(None,"\n")+"\n" #Saves the reply in an already easy to send format

    @property
    def target(self):
        return self.wit_response["outcomes"][0]["entities"]["target"][0]["value"]

An example of a very frequent key that can be found in the JSON is the target key (as it can be seen above). Since it doesn't always exist calling the target method will raise an exception. I'm looking for a solution to handle fields like that that'll be as elegant as possible.

At the moment I've just wrapped the the return statement in the target method with a try,except block and returns None if an exception is raised (the JSON didn't have a target key).

I've also considered using a decorator that adds the try,except block however it didn't seem to work when I added the decorator above the @property decorator:

def None_Return_Value_Decorator(func):
    def wrapper(self):
        try:
            value = func(self)
            return value
        except:
            return None

    return wrapper

Are there more elegant solutions? If not, can the decorator one work (and how do I fix it if it does)?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
matanc1
  • 6,525
  • 6
  • 37
  • 57

1 Answers1

2

Add your decorator below the @property decorator:

@property
@None_Return_Value_Decorator
def target(self):
    return self.wit_response["outcomes"][0]["entities"]["target"][0]["value"]

Now your decorator is applied first, before turning the return value of your decorator into the property getter.

You want to avoid using a except handler however; try to only catch a relevant exception, such as IndexError and KeyError.

You can make the exceptions configurable in the decorator:

from functools import wraps

def ignore_exception(*exceptions):
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kw):
            try:
                return f(*args, **kw)
            except exceptions:
                return None
        return wrapper
    return decorator

then use as:

@property
@ignore_exception(KeyError)
def target(self):
    return self.wit_response["outcomes"][0]["entities"]["target"][0]["value"]

for example.

Community
  • 1
  • 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • That seems to work. Are you aware of a better way to handle this than I already have? This seems very inelegant to me. – matanc1 Sep 01 '14 at 21:39
  • @Shookie: if you are using Python 3.4 you can use the new [`contextlib.suppress()` context manager](https://docs.python.org/3/library/contextlib.html#contextlib.suppress). – Martijn Pieters Sep 01 '14 at 21:41
  • @Shookie: but using a decorator to encapsulate common handling looks fine to me; you could make the decorator accept the exception(s) to catch as an argument even. – Martijn Pieters Sep 01 '14 at 21:43