|
14 | 14 | AuthenticationError, ForbiddenError, InvalidRequestError,
|
15 | 15 | NotFoundError, ServiceUnavailableError)
|
16 | 16 |
|
| 17 | +# global session |
| 18 | +session = None |
17 | 19 |
|
18 |
| -class Connection: |
19 |
| - @classmethod |
20 |
| - def request(cls, http_verb, url, **options): |
21 |
| - if 'headers' in options: |
22 |
| - headers = options['headers'] |
| 20 | + |
| 21 | +def request(http_verb, url, **options): |
| 22 | + if 'headers' in options: |
| 23 | + headers = options['headers'] |
| 24 | + else: |
| 25 | + headers = {} |
| 26 | + |
| 27 | + accept_value = 'application/json' |
| 28 | + if ApiConfig.api_version: |
| 29 | + accept_value += ", application/vnd.data.nasdaq+json;version=%s" % ApiConfig.api_version |
| 30 | + |
| 31 | + headers = Util.merge_to_dicts({ |
| 32 | + 'accept': accept_value, |
| 33 | + 'request-source': 'python', |
| 34 | + 'request-source-version': VERSION |
| 35 | + }, headers) |
| 36 | + if ApiConfig.api_key: |
| 37 | + headers = Util.merge_to_dicts({'x-api-token': ApiConfig.api_key}, headers) |
| 38 | + |
| 39 | + options['headers'] = headers |
| 40 | + |
| 41 | + abs_url = '%s/%s' % (ApiConfig.api_base, url) |
| 42 | + |
| 43 | + return execute_request(http_verb, abs_url, **options) |
| 44 | + |
| 45 | + |
| 46 | +def execute_request(http_verb, url, **options): |
| 47 | + session = get_session() |
| 48 | + |
| 49 | + try: |
| 50 | + response = session.request( |
| 51 | + method=http_verb, |
| 52 | + url=url, |
| 53 | + verify=ApiConfig.verify_ssl, |
| 54 | + **options |
| 55 | + ) |
| 56 | + if response.status_code < 200 or response.status_code >= 300: |
| 57 | + handle_api_error(response) |
23 | 58 | else:
|
24 |
| - headers = {} |
25 |
| - |
26 |
| - accept_value = 'application/json' |
27 |
| - if ApiConfig.api_version: |
28 |
| - accept_value += ", application/vnd.data.nasdaq+json;version=%s" % ApiConfig.api_version |
29 |
| - |
30 |
| - headers = Util.merge_to_dicts({'accept': accept_value, |
31 |
| - 'request-source': 'python', |
32 |
| - 'request-source-version': VERSION}, headers) |
33 |
| - if ApiConfig.api_key: |
34 |
| - headers = Util.merge_to_dicts({'x-api-token': ApiConfig.api_key}, headers) |
35 |
| - |
36 |
| - options['headers'] = headers |
37 |
| - |
38 |
| - abs_url = '%s/%s' % (ApiConfig.api_base, url) |
39 |
| - |
40 |
| - return cls.execute_request(http_verb, abs_url, **options) |
41 |
| - |
42 |
| - @classmethod |
43 |
| - def execute_request(cls, http_verb, url, **options): |
44 |
| - session = cls.get_session() |
45 |
| - |
46 |
| - try: |
47 |
| - response = session.request(method=http_verb, |
48 |
| - url=url, |
49 |
| - verify=ApiConfig.verify_ssl, |
50 |
| - **options) |
51 |
| - if response.status_code < 200 or response.status_code >= 300: |
52 |
| - cls.handle_api_error(response) |
53 |
| - else: |
54 |
| - return response |
55 |
| - except requests.exceptions.RequestException as e: |
56 |
| - if e.response: |
57 |
| - cls.handle_api_error(e.response) |
58 |
| - raise e |
59 |
| - |
60 |
| - @classmethod |
61 |
| - def get_session(cls): |
62 |
| - session = requests.Session() |
63 |
| - adapter = HTTPAdapter(max_retries=cls.get_retries()) |
64 |
| - session.mount(ApiConfig.api_protocol, adapter) |
65 |
| - |
66 |
| - proxies = urllib.request.getproxies() |
67 |
| - if proxies is not None: |
68 |
| - session.proxies.update(proxies) |
| 59 | + return response |
| 60 | + except requests.exceptions.RequestException as e: |
| 61 | + if e.response: |
| 62 | + handle_api_error(e.response) |
| 63 | + raise e |
| 64 | + |
| 65 | + |
| 66 | +def get_retries(): |
| 67 | + if not ApiConfig.use_retries: |
| 68 | + return Retry(total=0) |
| 69 | + |
| 70 | + Retry.BACKOFF_MAX = ApiConfig.max_wait_between_retries |
| 71 | + retries = Retry(total=ApiConfig.number_of_retries, |
| 72 | + connect=ApiConfig.number_of_retries, |
| 73 | + read=ApiConfig.number_of_retries, |
| 74 | + status_forcelist=ApiConfig.retry_status_codes, |
| 75 | + backoff_factor=ApiConfig.retry_backoff_factor, |
| 76 | + raise_on_status=False) |
69 | 77 |
|
| 78 | + return retries |
| 79 | + |
| 80 | + |
| 81 | +def get_session(): |
| 82 | + global session |
| 83 | + if session is not None: |
70 | 84 | return session
|
71 | 85 |
|
72 |
| - @classmethod |
73 |
| - def get_retries(cls): |
74 |
| - if not ApiConfig.use_retries: |
75 |
| - return Retry(total=0) |
76 |
| - |
77 |
| - Retry.BACKOFF_MAX = ApiConfig.max_wait_between_retries |
78 |
| - retries = Retry(total=ApiConfig.number_of_retries, |
79 |
| - connect=ApiConfig.number_of_retries, |
80 |
| - read=ApiConfig.number_of_retries, |
81 |
| - status_forcelist=ApiConfig.retry_status_codes, |
82 |
| - backoff_factor=ApiConfig.retry_backoff_factor, |
83 |
| - raise_on_status=False) |
84 |
| - |
85 |
| - return retries |
86 |
| - |
87 |
| - @classmethod |
88 |
| - def parse(cls, response): |
89 |
| - try: |
90 |
| - return response.json() |
91 |
| - except ValueError: |
92 |
| - raise DataLinkError(http_status=response.status_code, http_body=response.text) |
93 |
| - |
94 |
| - @classmethod |
95 |
| - def handle_api_error(cls, resp): |
96 |
| - error_body = cls.parse(resp) |
97 |
| - |
98 |
| - # if our app does not form a proper data_link_error response |
99 |
| - # throw generic error |
100 |
| - if 'error' not in error_body: |
101 |
| - raise DataLinkError(http_status=resp.status_code, http_body=resp.text) |
102 |
| - |
103 |
| - code = error_body['error']['code'] |
104 |
| - message = error_body['error']['message'] |
105 |
| - prog = re.compile('^QE([a-zA-Z])x') |
106 |
| - if prog.match(code): |
107 |
| - code_letter = prog.match(code).group(1) |
108 |
| - |
109 |
| - d_klass = { |
110 |
| - 'L': LimitExceededError, |
111 |
| - 'M': InternalServerError, |
112 |
| - 'A': AuthenticationError, |
113 |
| - 'P': ForbiddenError, |
114 |
| - 'S': InvalidRequestError, |
115 |
| - 'C': NotFoundError, |
116 |
| - 'X': ServiceUnavailableError |
117 |
| - } |
118 |
| - klass = d_klass.get(code_letter, DataLinkError) |
119 |
| - |
120 |
| - raise klass(message, resp.status_code, resp.text, resp.headers, code) |
| 86 | + session = requests.Session() |
| 87 | + adapter = HTTPAdapter(max_retries=get_retries()) |
| 88 | + session.mount(ApiConfig.api_protocol, adapter) |
| 89 | + |
| 90 | + proxies = urllib.request.getproxies() |
| 91 | + if proxies is not None: |
| 92 | + session.proxies.update(proxies) |
| 93 | + |
| 94 | + return session |
| 95 | + |
| 96 | + |
| 97 | +def parse(response): |
| 98 | + try: |
| 99 | + return response.json() |
| 100 | + except ValueError: |
| 101 | + raise DataLinkError(http_status=response.status_code, http_body=response.text) |
| 102 | + |
| 103 | + |
| 104 | +def handle_api_error(resp): |
| 105 | + error_body = parse(resp) |
| 106 | + |
| 107 | + # if our app does not form a proper data_link_error response |
| 108 | + # throw generic error |
| 109 | + if 'error' not in error_body: |
| 110 | + raise DataLinkError(http_status=resp.status_code, http_body=resp.text) |
| 111 | + |
| 112 | + code = error_body['error']['code'] |
| 113 | + message = error_body['error']['message'] |
| 114 | + prog = re.compile('^QE([a-zA-Z])x') |
| 115 | + if prog.match(code): |
| 116 | + code_letter = prog.match(code).group(1) |
| 117 | + |
| 118 | + d_klass = { |
| 119 | + 'L': LimitExceededError, |
| 120 | + 'M': InternalServerError, |
| 121 | + 'A': AuthenticationError, |
| 122 | + 'P': ForbiddenError, |
| 123 | + 'S': InvalidRequestError, |
| 124 | + 'C': NotFoundError, |
| 125 | + 'X': ServiceUnavailableError |
| 126 | + } |
| 127 | + klass = d_klass.get(code_letter, DataLinkError) |
| 128 | + |
| 129 | + raise klass(message, resp.status_code, resp.text, resp.headers, code) |
0 commit comments