def construct_api_url(api_url, **params): """Construct a Twitter API url, encoded, with parameters :param api_url: URL of the Twitter API endpoint you are attempting to construct :param \*\*params: Parameters that are accepted by Twitter for the endpoint you're requesting :rtype: string Usage:: >>> from birdieapp.twython import Twython >>> twitter = Twython() >>> api_url = 'https://api.twitter.com/1.1/search/tweets.json' >>> constructed_url = twitter.construct_api_url(api_url, q='python', result_type='popular') >>> print constructed_url https://api.twitter.com/1.1/search/tweets.json?q=python&result_type=popular """ querystring = [] params, _ = _transparent_params(params or {}) params = requests.utils.to_key_val_list(params) for (k, v) in params: querystring.append( '%s=%s' % (Twython.encode(k), quote_plus(Twython.encode(v))) ) return '%s?%s' % (api_url, '&'.join(querystring))
def _request(self, url, method='GET', params=None): """Internal stream request handling""" self.connected = True retry_counter = 0 method = method.lower() func = getattr(self.client, method) params, _ = _transparent_params(params) def _send(retry_counter): requests_args = {} for k, v in self.client_args.items(): # Maybe this should be set as a class variable and only done once? if k in ('timeout', 'allow_redirects', 'verify'): requests_args[k] = v while self.connected: try: if method == 'get': requests_args['params'] = params else: requests_args['data'] = params response = func(url, **requests_args) except requests.exceptions.Timeout: self.on_timeout() else: if response.status_code != 200: self.on_error(response.status_code, response.content) if self.retry_count and (self.retry_count - retry_counter) > 0: time.sleep(self.retry_in) retry_counter += 1 _send(retry_counter) return response while self.connected: response = _send(retry_counter) for line in response.iter_lines(self.chunk_size): if not self.connected: break if line: try: if is_py3: line = line.decode('utf-8') data = json.loads(line) except ValueError: # pragma: no cover self.on_error(response.status_code, 'Unable to decode response, not valid JSON.') else: if self.on_success(data): # pragma: no cover for message_type in self.handlers: if message_type in data: handler = getattr(self, 'on_' + message_type, None) if handler and callable(handler) and not handler(data.get(message_type)): break response.close()
def _request(self, url, method='GET', params=None, api_call=None): """Internal request method""" method = method.lower() params = params or {} func = getattr(self.client, method) params, files = _transparent_params(params) requests_args = {} for k, v in self.client_args.items(): # Maybe this should be set as a class variable and only done once? if k in ('timeout', 'allow_redirects', 'stream', 'verify'): requests_args[k] = v if method == 'get': requests_args['params'] = params else: requests_args.update({ 'data': params, 'files': files, }) try: response = func(url, **requests_args) except requests.RequestException as e: raise TwythonError(str(e)) content = response.content.decode('utf-8') # create stash for last function intel self._last_call = { 'api_call': api_call, 'api_error': None, 'cookies': response.cookies, 'headers': response.headers, 'status_code': response.status_code, 'url': response.url, 'content': content, } # Wrap the json loads in a try, and defer an error # Twitter will return invalid json with an error code in the headers json_error = False try: try: # try to get json content = content.json() except AttributeError: # if unicode detected content = json.loads(content) except ValueError: json_error = True content = {} if response.status_code > 304: # If there is no error message, use a default. errors = content.get('errors', [{'message': 'An error occurred processing your request.'}]) if errors and isinstance(errors, list): error_message = errors[0]['message'] else: error_message = errors # pragma: no cover self._last_call['api_error'] = error_message ExceptionType = TwythonError if response.status_code == 429: # Twitter API 1.1, always return 429 when rate limit is exceeded ExceptionType = TwythonRateLimitError # pragma: no cover elif response.status_code == 401 or 'Bad Authentication data' in error_message: # Twitter API 1.1, returns a 401 Unauthorized or # a 400 "Bad Authentication data" for invalid/expired app keys/user tokens ExceptionType = TwythonAuthError raise ExceptionType(error_message, error_code=response.status_code, retry_after=response.headers.get('retry-after')) # if we have a json error here, then it's not an official Twitter API error if json_error and not response.status_code in (200, 201, 202): # pragma: no cover raise TwythonError('Response was not valid JSON, unable to decode.') return content
def _request(self, url, method='GET', params=None): """Internal stream request handling""" self.connected = True retry_counter = 0 method = method.lower() func = getattr(self.client, method) params, _ = _transparent_params(params) def _send(retry_counter): requests_args = {} for k, v in self.client_args.items(): # Maybe this should be set as a class variable and only done once? if k in ('timeout', 'allow_redirects', 'verify'): requests_args[k] = v while self.connected: try: if method == 'get': requests_args['params'] = params else: requests_args['data'] = params response = func(url, **requests_args) except requests.exceptions.Timeout: self.on_timeout() else: if response.status_code != 200: self.on_error(response.status_code, response.content) if self.retry_count and (self.retry_count - retry_counter) > 0: time.sleep(self.retry_in) retry_counter += 1 _send(retry_counter) return response while self.connected: response = _send(retry_counter) for line in response.iter_lines(self.chunk_size): if not self.connected: break if line: try: if is_py3: line = line.decode('utf-8') data = json.loads(line) except ValueError: # pragma: no cover self.on_error( response.status_code, 'Unable to decode response, not valid JSON.') else: if self.on_success(data): # pragma: no cover for message_type in self.handlers: if message_type in data: handler = getattr(self, 'on_' + message_type, None) if handler and callable( handler) and not handler( data.get(message_type)): break response.close()