1# Tweepy
2# Copyright 2009-2020 Joshua Roesslein
3# See LICENSE for details.
4
5import json as json_lib
6
7from tweepy.error import TweepError
8from tweepy.models import ModelFactory
9
10
11class Parser(object):
12
13    def parse(self, method, payload, *args, **kwargs):
14        """
15        Parse the response payload and return the result.
16        Returns a tuple that contains the result data and the cursors
17        (or None if not present).
18        """
19        raise NotImplementedError
20
21    def parse_error(self, payload):
22        """
23        Parse the error message and api error code from payload.
24        Return them as an (error_msg, error_code) tuple. If unable to parse the
25        message, throw an exception and default error message will be used.
26        """
27        raise NotImplementedError
28
29
30class RawParser(Parser):
31
32    def __init__(self):
33        pass
34
35    def parse(self, method, payload, *args, **kwargs):
36        return payload
37
38    def parse_error(self, payload):
39        return payload
40
41
42class JSONParser(Parser):
43
44    payload_format = 'json'
45
46    def parse(self, method, payload, return_cursors=False):
47        try:
48            json = json_lib.loads(payload)
49        except Exception as e:
50            raise TweepError('Failed to parse JSON payload: %s' % e)
51
52        if return_cursors and isinstance(json, dict):
53            if 'next' in json:
54                return json, json['next']
55            elif 'next_cursor' in json:
56                if 'previous_cursor' in json:
57                    cursors = json['previous_cursor'], json['next_cursor']
58                    return json, cursors
59                else:
60                    return json, json['next_cursor']
61        return json
62
63    def parse_error(self, payload):
64        error_object = json_lib.loads(payload)
65
66        if 'error' in error_object:
67            reason = error_object['error']
68            api_code = error_object.get('code')
69        else:
70            reason = error_object['errors']
71            api_code = [error.get('code') for error in
72                        reason if error.get('code')]
73            api_code = api_code[0] if len(api_code) == 1 else api_code
74
75        return reason, api_code
76
77
78class ModelParser(JSONParser):
79
80    def __init__(self, model_factory=None):
81        JSONParser.__init__(self)
82        self.model_factory = model_factory or ModelFactory
83
84    def parse(self, method, payload, return_cursors=False):
85        try:
86            if method.payload_type is None:
87                return
88            model = getattr(self.model_factory, method.payload_type)
89        except AttributeError:
90            raise TweepError('No model for this payload type: '
91                             '%s' % method.payload_type)
92
93        json = JSONParser.parse(self, method, payload, return_cursors=return_cursors)
94        if isinstance(json, tuple):
95            json, cursors = json
96        else:
97            cursors = None
98
99        if method.payload_list:
100            result = model.parse_list(method.api, json)
101        else:
102            result = model.parse(method.api, json)
103
104        if cursors:
105            return result, cursors
106        else:
107            return result
108