def request(self, method, api_endpoint, **kwargs): """Wrapper for underlying :class:`requests.Session` Handles generating full API URL, session reuse and auth, request defaults, and invalid response status codes Used throughout library as the core underlying request/response method for all interactions with server Args: method (str): Request method (get, post, put, etc.) api_endpoint (str): Portion of URL matching API endpoint route as listed in platform /docs help page **kwargs (dict): Remaining arguments passed through to actual request call Notes: All other provided kwargs are passed to underlying :meth:`requests.Session.request()` call Raises: swimlane.exceptions.SwimlaneHTTP400Error: On 400 responses with additional context about the exception requests.HTTPError: Any other 4xx/5xx HTTP responses Returns: requests.Response: Successful response instances Examples: Request and parse server settings endpoint response >>> server_settings = swimlane.request('get', 'settings').json() """ while api_endpoint.startswith('/'): api_endpoint = api_endpoint[1:] # Ensure a timeout is set kwargs.setdefault('timeout', self._default_timeout) # Manually grab and dump json data to have full control over serialization # Emulate default requests behavior json_data = kwargs.pop('json', None) if json_data is not None: headers = CaseInsensitiveDict(kwargs.get('headers', {})) headers.setdefault('Content-Type', 'application/json') kwargs['headers'] = headers kwargs['data'] = json.dumps(json_data, sort_keys=True, separators=(',', ':')) response = self._session.request( method, urljoin(str(self.host) + self._api_root, api_endpoint), **kwargs) # Roll 400 errors up into SwimlaneHTTP400Errors with specific Swimlane error code support try: response.raise_for_status() except requests.HTTPError as error: if error.response.status_code == 400: raise SwimlaneHTTP400Error(error) else: raise error return response
def test_setdefault(self): cid = CaseInsensitiveDict({'Spam': 'blueval'}) self.assertEqual( cid.setdefault('spam', 'notblueval'), 'blueval' ) self.assertEqual( cid.setdefault('notspam', 'notblueval'), 'notblueval' )
def _prepare_request(self, command, json, opcode_name, fetch_list, **kwargs): kwargs = CaseInsensitiveDict(kwargs) kwargs.update({ 'apiKey': self.key, opcode_name: command, }) if json: kwargs['response'] = 'json' if 'page' in kwargs or fetch_list: kwargs.setdefault('pagesize', 500) kwarg = 'params' if self.method == 'get' else 'data' return kwarg, dict(kwargs._store.values())
def _prepare_request(self, command, json, opcode_name, fetch_list, **kwargs): params = CaseInsensitiveDict(**kwargs) params.update({ 'apiKey': self.key, opcode_name: command, }) if json: params['response'] = 'json' if 'page' in kwargs or fetch_list: params.setdefault('pagesize', PAGE_SIZE) kind = 'params' if self.method == 'get' else 'data' return kind, {k: v for k, v in params.items()}
def _prepare_request(self, command, json=True, opcode_name='command', fetch_list=False, **kwargs): params = CaseInsensitiveDict(**kwargs) params.update({ 'apiKey': self.key, opcode_name: command, }) if json: params['response'] = 'json' if 'page' in kwargs or fetch_list: params.setdefault('pagesize', PAGE_SIZE) if 'expires' not in params and self.expiration.total_seconds() >= 0: params['signatureVersion'] = '3' tz = pytz.utc expires = tz.localize(datetime.utcnow() + self.expiration) params['expires'] = expires.astimezone(tz).strftime(EXPIRES_FORMAT) kind = 'params' if self.method == 'get' else 'data' return kind, dict(params.items())
def _get_email_headers_from_part(self, part): email_headers = list(part.items()) if not email_headers: return {} # Convert the header tuple into a dictionary headers = CaseInsensitiveDict() # assume duplicate header names with unique values. ex: received for x in email_headers: try: headers.setdefault(x[0].lower().replace('-', '_').replace(' ', '_'), []).append(x[1]) except Exception as e: error_msg = self._get_error_message_from_exception(e) err = "Error occurred while converting the header tuple into a dictionary" self.debug_print("{}. {}".format(err, error_msg)) headers = {k.lower(): '\n'.join(v) for k, v in headers.items()} return dict(headers)
def test_setdefault(self): cid = CaseInsensitiveDict({"Spam": "blueval"}) assert cid.setdefault("spam", "notblueval") == "blueval" assert cid.setdefault("notspam", "notblueval") == "notblueval"
def test_setdefault(self): cid = CaseInsensitiveDict({'Spam': 'blueval'}) assert cid.setdefault('spam', 'notblueval') == 'blueval' assert cid.setdefault('notspam', 'notblueval') == 'notblueval'
class NEB_enzyme_manager(object): def __init__(self, pickle_filepath=None): # requests.structures.CaseInsensitiveDict # <name>: {'product_table': ..., 'url': ..., ', 'cat_entry': ..., } self.Pickle_filepath = pickle_filepath self.Enzymes = CaseInsensitiveDict() if pickle_filepath: try: self.load_enzymes() except IOError as e: print("Could not load initial enzymes from %s:: %s: '%s'", pickle_filepath, type(e), e) def get(self, name): """ Get enzyme by name. """ if name in self.Enzymes: return self.Enzymes[name] url = self.search_product_url(name) if url: return self.Enzymes[name] def search_product_url(self, name): """ Search NEB website for product with name <name> and return product page url. """ if self.Enzymes.get(name) and self.Enzymes[name].get('url'): return self.Enzymes[name]['url'] url = neb_search_product_url(name) if url: self.Enzymes.setdefault(name, {})['url'] = url return url def get_product_table(self, name, entryid=None): """ Get product table for enzyma <name> """ if self.Enzymes.get(name) and self.Enzymes[name].get('product_table'): return self.Enzymes[name]['product_table'] if entryid: table = product_table(entryid) else: product_url = self.search_product_url(name) if not product_url: print("Could not find product name", name) return table = product_table(product_url) # Save and return self.Enzymes.setdefault(name, {})['product_table'] = table return table def get_price(self, name): """ Return price of <name> (using the first row in product table) """ pt = self.get_product_table(name) return pt[0]['Price'] def get_catno(self, name): """ Return price of <name> (using the first row in product table) """ pt = self.get_product_table(name) return pt[0]['Catalog #'] def get_size(self, name): """ Return price of <name> (using the first row in product table) """ pt = self.get_product_table(name) return pt[0]['Size'] def get_unit_price(self, name): """ Return unit price as $/unit """ if self.Enzymes.get(name) and self.Enzymes[name].get('unit_price'): return self.Enzymes[name]['unit_price'] table = self.get_product_table(name) up = unit_price(table) # Save and return self.Enzymes.setdefault(name, {})['unit_price'] = up return up def get_order_info_str(self, name): """ Return a standard price/order string for enzyme <name>. """ return " {:7}: {:<6} ({}, {} for {}, {})"\ .format(name, self.get_unit_price(name), self.get_catno(name), self.get_price(name), self.get_size(name), self.search_product_url(name)) def load_enzymes(self, filepath=None): """ Load enzymes data from filepath or self.Pickle_filepath and merge with self.Enzymes. """ if filepath is None: filepath = self.Pickle_filepath with open(filepath, 'rb') as fd: enzymes = pickle.load(fd) self.Enzymes.update(enzymes) print("%s enzymes loaded from file %s" % (len(enzymes), filepath)) self.Pickle_filepath = filepath def save_enzymes(self, filepath=None): """ Save enzymes data to filepath or self.Pickle_filepath """ if filepath is None: filepath = self.Pickle_filepath with open(filepath, 'wb') as fd: pickle.dump(self.Enzymes, fd) print("%s enzymes saved to file %s" % (len(self.Enzymes), filepath)) self.Pickle_filepath = filepath
def request( self, path: str, operation: str, method="GET", expected_status=200, request_kwargs: Optional[dict] = None, **kwargs, ) -> Union[List[Object], Object]: """ Make the HTTP request using requests. The URL is created based on the path and base URL and any defaults from the OAS schema are injected. :return: a list or dict, the result of calling response.json() :raises: :class:`requests.HTTPException` for internal server errors :raises: :class:`ClientError` for HTTP 4xx status codes """ url = urljoin(self.base_url, path) if request_kwargs: kwargs.update(request_kwargs) headers = CaseInsensitiveDict(kwargs.pop("headers", {})) headers.setdefault("Accept", "application/json") headers.setdefault("Content-Type", "application/json") schema_headers = get_headers(self.schema, operation) for header, value in schema_headers.items(): headers.setdefault(header, value) if self.auth: headers.update(self.auth.credentials()) kwargs["headers"] = headers pre_id = self.pre_request(method, url, **kwargs) response = requests.request(method, url, **kwargs) try: response_json = response.json() except Exception: response_json = None self.post_response(pre_id, response_json) self._log.add( self.service, url, method, dict(headers), copy.deepcopy(kwargs.get("data", kwargs.get("json", None))), response.status_code, dict(response.headers), response_json, params=kwargs.get("params"), ) try: response.raise_for_status() except requests.HTTPError as exc: if response.status_code >= 500: raise raise ClientError(response_json) from exc assert response.status_code == expected_status, response_json return response_json
class MockResponse(MagicMock): """Mock a response. This provides an object to mock a response given a real response and values that want to be changed. All attributes passed have priority over the real responses, which are used as a fallback, except when it comes to headers. With headers, any headers that are not set are taken from the real response if they exist. This is to ensure there won't be inconsistencies with the necessary headers. Recommended usage is as follows: 1. Record the request(s) that need to be mocked via the betamax() factory decorator in helper.py 2. Pass pass_recorder=True to betamax() and add a recorder argument to the test function 3. The responses that you may wish to mock are available as the 'recorded_response' attribute on the recorder's current cassette interaction. It's easiest to determine the interaction that you will need to mock via a debugger. The request content is determined from it's json, which is given as a dictionary/list. This is because all reddit api responses are valid json. If you must, you can set the text attribute as well, but this is only used if json is None. You can use the `as_context` method to use a context manager, or `as_decorator` to use this as a decorator and pass the mocked response as an extra argument. """ def __init__(self, real_response, status_code=None, json=None, raise_for_status=None, close=None, reason=None, cookies=None, apparent_encoding=None, encoding=None, history=None, headers=None, elapsed=None, _content_consumed=None, parent=None, text=None, **kwargs): super(MockResponse, self).__init__(spec=real_response) self.real_response = real_response self.apparent_encoding = apparent_encoding or \ real_response.apparent_encoding self.close = close or real_response.close self.json = (lambda: json) if json else real_response.json self.text = dumps(self.json()) if json else text self._content_consumed = _content_consumed or \ real_response._content_consumed self.content = self.text.encode() self.parent = parent for k, v in kwargs.items(): setattr(self, k, v) self._content = self.content self.history = history if history else \ ([] or real_response.history) self.elapsed = elapsed if elapsed else \ (type(real_response.elapsed)() or real_response.elapsed) self.cookies = cookies if cookies else \ (type(real_response.cookies)() or real_response.cookies) self.encoding = encoding or real_response.encoding self.iter_content = iter(self.content) self.iter_lines = iter([self.content]) self.raise_for_status = raise_for_status or \ real_response.raise_for_status self.raw = real_response.raw self.status_code = status_code or real_response.status_code self.headers = CaseInsensitiveDict(headers) if headers else \ CaseInsensitiveDict() for name, val in real_response.headers.items(): self.headers.setdefault(name, val) self.reason = reason or real_response.reason self.is_permanent_redirect = \ ('location' in self.headers and self.status_code in [301, 308]) self.ok = 200 <= self.status_code < 300 self.is_redirect = real_response.is_redirect @classmethod def in_place(cls, holder, attribute='recorded_response', *args, **kwargs): setattr(holder, attribute, cls(getattr(holder, attribute), *args, **kwargs)) @classmethod def as_context(cls, holder, attribute='recorded_response', *args, **kwargs): class in_context(object): def __init__(self, holder, attribute, *args, **kwargs): self.holder, self.attribute, self.args, self.kwargs = \ holder, attribute, args, kwargs def __enter__(self): cls.in_place(self.holder, self.attribute, *self.args, **self.kwargs) return getattr(self.holder, self.attribute) def __exit__(self, exc_type, exc_val, exc_trace): setattr(self.holder, self.attribute, getattr(self.holder, self.attribute).real_response) return in_context(holder, attribute, *args, **kwargs) @classmethod def as_decorator(cls, holder, attribute='recorded_response', *args, **kwargs): def factory(func): @wraps(func) def wrapped(*a, **kw): with cls.as_context(holder, attribute, *args, **kwargs) as resp: a = list(a) a.append(resp) a = tuple(a) return func(*a, **kw) return wrapped return factory
def test_setdefault(self): cid = CaseInsensitiveDict({"Spam": "blueval"}) self.assertEqual(cid.setdefault("spam", "notblueval"), "blueval") self.assertEqual(cid.setdefault("notspam", "notblueval"), "notblueval")