From f27eae46dce6c107b6823398da7b399fd60aaab1 Mon Sep 17 00:00:00 2001 From: bartonip Date: Sun, 28 Jun 2020 07:03:49 +0000 Subject: [PATCH] Fixed linting issues --- pyodata/v2/service.py | 885 +++++++++++++++++++++++++++++------------- 1 file changed, 609 insertions(+), 276 deletions(-) diff --git a/pyodata/v2/service.py b/pyodata/v2/service.py index 26f79b3c..4a215948 100644 --- a/pyodata/v2/service.py +++ b/pyodata/v2/service.py @@ -24,7 +24,7 @@ from pyodata.exceptions import HttpError, PyODataException, ExpressionError from . import model -LOGGER_NAME = 'pyodata.service' +LOGGER_NAME = "pyodata.service" HTTP_CODE_OK = 200 HTTP_CODE_CREATED = 201 @@ -33,7 +33,7 @@ def urljoin(*path): """Joins the passed string parts into a one string url""" - return '/'.join((part.strip('/') for part in path)) + return "/".join((part.strip("/") for part in path)) def encode_multipart(boundary, http_requests): @@ -41,31 +41,40 @@ def encode_multipart(boundary, http_requests): lines = [] - lines.append('') + lines.append("") for req in http_requests: - lines.append('--{0}'.format(boundary)) + lines.append("--{0}".format(boundary)) if not isinstance(req, MultipartRequest): - lines.extend(('Content-Type: application/http ', 'Content-Transfer-Encoding:binary')) + lines.extend( + ("Content-Type: application/http ", "Content-Transfer-Encoding:binary") + ) - lines.append('') + lines.append("") # request line (method + path + query params) - line = '{method} {path}'.format(method=req.get_method(), path=req.get_path()) - query_params = '&'.join(['{}={}'.format(key, val) for key, val in req.get_query_params().items()]) + line = "{method} {path}".format( + method=req.get_method(), path=req.get_path() + ) + query_params = "&".join( + [ + "{}={}".format(key, val) + for key, val in req.get_query_params().items() + ] + ) if query_params: - line += '?' + query_params - line += ' HTTP/1.1' + line += "?" + query_params + line += " HTTP/1.1" lines.append(line) # request specific headers for hdr, hdr_val in req.get_headers().items(): - lines.append('{}: {}'.format(hdr, hdr_val)) + lines.append("{}: {}".format(hdr, hdr_val)) - lines.append('') + lines.append("") body = req.get_body() if body is not None: @@ -74,11 +83,11 @@ def encode_multipart(boundary, http_requests): # this is very important since SAP gateway rejected request witout this line. It seems # blank line must be provided as a representation of emtpy body, else we are getting # 400 Bad fromat from SAP gateway - lines.append('') + lines.append("") - lines.append('--{0}--'.format(boundary)) + lines.append("--{0}--".format(boundary)) - return '\r\n'.join(lines) + return "\r\n".join(lines) def decode_multipart(data, content_type): @@ -89,7 +98,7 @@ def decode(message): messages = [] for i, part in enumerate(message.walk()): # pylint: disable=unused-variable - if part.get_content_type() == 'multipart/mixed': + if part.get_content_type() == "multipart/mixed": for submessage in part.get_payload(): messages.append(decode(submessage)) break @@ -123,7 +132,7 @@ class FakeSocket: """Fake socket to simulate received http response content""" def __init__(self, response_str): - self._file = BytesIO(response_str.encode('utf-8')) + self._file = BytesIO(response_str.encode("utf-8")) def makefile(self, *args, **kwargs): """Fake file that provides string content""" @@ -138,7 +147,9 @@ def makefile(self, *args, **kwargs): return ODataHttpResponse( response.getheaders(), response.status, - response.read(len(data)) # the len here will give a 'big enough' value to read the whole content + response.read( + len(data) + ), # the len here will give a 'big enough' value to read the whole content ) def json(self): @@ -148,7 +159,7 @@ def json(self): # approach can bring issues with encoding # https://github.com/requests/requests/blob/master/requests/models.py#L868 if self.content: - return json.loads(self.content.decode('utf-8')) + return json.loads(self.content.decode("utf-8")) return None @@ -180,9 +191,15 @@ def __init__(self, entity_type, single_key=None, **args): # check that entity type key consists of exactly one property if len(self._key) != 1: - raise PyODataException(('Key of entity type {} consists of multiple properties {} ' - 'and cannot be initialized by single value').format( - self._entity_type.name, ', '.join([prop.name for prop in self._key]))) + raise PyODataException( + ( + "Key of entity type {} consists of multiple properties {} " + "and cannot be initialized by single value" + ).format( + self._entity_type.name, + ", ".join([prop.name for prop in self._key]), + ) + ) # get single key property and format key string key_prop = self._key[0] @@ -190,12 +207,20 @@ def __init__(self, entity_type, single_key=None, **args): self._type = EntityKey.TYPE_SINGLE - self._logger.debug(('Detected single property key, adding pair %s->%s to key' - 'properties'), key_prop.name, single_key) + self._logger.debug( + ( + "Detected single property key, adding pair %s->%s to key" + "properties" + ), + key_prop.name, + single_key, + ) else: for key_prop in self._key: if key_prop.name not in args: - raise PyODataException('Missing value for key property {}'.format(key_prop.name)) + raise PyODataException( + "Missing value for key property {}".format(key_prop.name) + ) self._type = EntityKey.TYPE_COMPLEX @@ -219,14 +244,17 @@ def to_key_string_without_parentheses(self): # raise RuntimeError('Entity key is not complete, missing value of property: {0}'.format(key_prop.name)) key_pairs.append( - '{0}={1}'.format(key_prop.name, key_prop.to_literal(self._proprties[key_prop.name]))) + "{0}={1}".format( + key_prop.name, key_prop.to_literal(self._proprties[key_prop.name]) + ) + ) - return ','.join(key_pairs) + return ",".join(key_pairs) def to_key_string(self): """Gets the string representation of the key, including parentheses""" - return '({})'.format(self.to_key_string_without_parentheses()) + return "({})".format(self.to_key_string_without_parentheses()) def __repr__(self): return self.to_key_string() @@ -250,7 +278,7 @@ def handler(self): def get_path(self): """Get path of the HTTP request""" # pylint: disable=no-self-use - return '' + return "" def get_query_params(self): """Get query params""" @@ -260,7 +288,7 @@ def get_query_params(self): def get_method(self): """Get HTTP method""" # pylint: disable=no-self-use - return 'GET' + return "GET" def get_body(self): """Get HTTP body or None if not applicable""" @@ -273,6 +301,8 @@ def get_headers(self): return None def add_headers(self, value): + """Add HTTP headers to request""" + if not isinstance(value, dict): raise TypeError("Headers must be of type 'dict' not {}".format(type(value))) @@ -297,25 +327,26 @@ def execute(self): if extra_headers is not None: headers.update(extra_headers) - self._logger.debug('Send (execute) %s request to %s', self.get_method(), url) - self._logger.debug(' query params: %s', self.get_query_params()) - self._logger.debug(' headers: %s', headers) + self._logger.debug("Send (execute) %s request to %s", self.get_method(), url) + self._logger.debug(" query params: %s", self.get_query_params()) + self._logger.debug(" headers: %s", headers) if body: - self._logger.debug(' body: %s', body) + self._logger.debug(" body: %s", body) - params = "&".join("%s=%s" % (k,v) for k,v in self.get_query_params().items()) + params = "&".join("%s=%s" % (k, v) for k, v in self.get_query_params().items()) response = self._connection.request( - self.get_method(), url, headers=headers, params=params, data=body) + self.get_method(), url, headers=headers, params=params, data=body + ) - self._logger.debug('Received response') - self._logger.debug(' url: %s', response.url) - self._logger.debug(' headers: %s', response.headers) - self._logger.debug(' status code: %d', response.status_code) + self._logger.debug("Received response") + self._logger.debug(" url: %s", response.url) + self._logger.debug(" headers: %s", response.headers) + self._logger.debug(" status code: %d", response.status_code) try: - self._logger.debug(' body: %s', response.content.decode('utf-8')) + self._logger.debug(" body: %s", response.content.decode("utf-8")) except UnicodeDecodeError: - self._logger.debug(' body: ') + self._logger.debug(" body: ") return self._handler(response) @@ -324,15 +355,19 @@ class EntityGetRequest(ODataHttpRequest): """Used for GET operations of a single entity""" def __init__(self, handler, entity_key, entity_set_proxy): - super(EntityGetRequest, self).__init__(entity_set_proxy.service.url, entity_set_proxy.service.connection, - handler) + super(EntityGetRequest, self).__init__( + entity_set_proxy.service.url, entity_set_proxy.service.connection, handler + ) self._logger = logging.getLogger(LOGGER_NAME) self._entity_key = entity_key self._entity_set_proxy = entity_set_proxy self._select = None self._expand = None - self._logger.debug('New instance of EntityGetRequest for last segment: %s', self._entity_set_proxy.last_segment) + self._logger.debug( + "New instance of EntityGetRequest for last segment: %s", + self._entity_set_proxy.last_segment, + ) def nav(self, nav_property): """Navigates to given navigation property and returns the EntitySetProxy""" @@ -358,16 +393,16 @@ def get_path(self): return self._entity_set_proxy.last_segment + self._entity_key.to_key_string() def get_headers(self): - return {'Accept': 'application/json', **self._headers} + return {"Accept": "application/json", **self._headers} def get_query_params(self): qparams = super(EntityGetRequest, self).get_query_params() if self._select is not None: - qparams['$select'] = self._select + qparams["$select"] = self._select if self._expand is not None: - qparams['$expand'] = self._expand + qparams["$expand"] = self._expand return qparams @@ -381,15 +416,18 @@ def stream_handler(response): """Returns $value from HTTP Response""" if response.status_code != HTTP_CODE_OK: - raise HttpError('HTTP GET for $value failed with status code {}' - .format(response.status_code), response) + raise HttpError( + "HTTP GET for $value failed with status code {}".format( + response.status_code + ), + response, + ) return response return ODataHttpRequest( - urljoin(self._url, self.get_path(), '/$value'), - connection, - stream_handler) + urljoin(self._url, self.get_path(), "/$value"), connection, stream_handler + ) class NavEntityGetRequest(EntityGetRequest): @@ -401,7 +439,9 @@ def __init__(self, handler, master_key, entity_set_proxy, nav_property): self._nav_property = nav_property def get_path(self): - return "{}/{}".format(super(NavEntityGetRequest, self).get_path(), self._nav_property) + return "{}/{}".format( + super(NavEntityGetRequest, self).get_path(), self._nav_property + ) class EntityCreateRequest(ODataHttpRequest): @@ -426,15 +466,18 @@ def __init__(self, url, connection, handler, entity_set, last_segment=None): # get all properties declared by entity type self._type_props = self._entity_type.proprties() - self._logger.debug('New instance of EntityCreateRequest for entity type: %s on path %s', self._entity_type.name, - self._last_segment) + self._logger.debug( + "New instance of EntityCreateRequest for entity type: %s on path %s", + self._entity_type.name, + self._last_segment, + ) def get_path(self): return self._last_segment def get_method(self): # pylint: disable=no-self-use - return 'POST' + return "POST" def _get_body(self): """Recursively builds a dictionary of values where some of the values @@ -455,7 +498,12 @@ def get_body(self): return json.dumps(self._get_body()) def get_headers(self): - return {'Accept': 'application/json', 'Content-Type': 'application/json', 'X-Requested-With': 'X', **self._headers} + return { + "Accept": "application/json", + "Content-Type": "application/json", + "X-Requested-With": "X", + **self._headers, + } @staticmethod def _build_values(entity_type, entity): @@ -465,7 +513,9 @@ def _build_values(entity_type, entity): """ if isinstance(entity, list): - return [EntityCreateRequest._build_values(entity_type, item) for item in entity] + return [ + EntityCreateRequest._build_values(entity_type, item) for item in entity + ] values = {} for key, val in entity.items(): @@ -476,8 +526,11 @@ def _build_values(entity_type, entity): nav_prop = entity_type.nav_proprty(key) val = EntityCreateRequest._build_values(nav_prop.typ, val) except KeyError: - raise PyODataException('Property {} is not declared in {} entity type'.format( - key, entity_type.name)) + raise PyODataException( + "Property {} is not declared in {} entity type".format( + key, entity_type.name + ) + ) values[key] = val @@ -503,14 +556,17 @@ def __init__(self, url, connection, handler, entity_set, entity_key): self._entity_set = entity_set self._entity_key = entity_key - self._logger.debug('New instance of EntityDeleteRequest for entity type: %s', entity_set.entity_type.name) + self._logger.debug( + "New instance of EntityDeleteRequest for entity type: %s", + entity_set.entity_type.name, + ) def get_path(self): return self._entity_set.name + self._entity_key.to_key_string() def get_method(self): # pylint: disable=no-self-use - return 'DELETE' + return "DELETE" class EntityModifyRequest(ODataHttpRequest): @@ -519,9 +575,11 @@ class EntityModifyRequest(ODataHttpRequest): Call execute() to send the update-request to the OData service and get the modified entity.""" - ALLOWED_HTTP_METHODS = ['PATCH', 'PUT', 'MERGE'] + ALLOWED_HTTP_METHODS = ["PATCH", "PUT", "MERGE"] - def __init__(self, url, connection, handler, entity_set, entity_key, method="PATCH"): + def __init__( + self, url, connection, handler, entity_set, entity_key, method="PATCH" + ): super(EntityModifyRequest, self).__init__(url, connection, handler) self._logger = logging.getLogger(LOGGER_NAME) self._entity_set = entity_set @@ -530,15 +588,21 @@ def __init__(self, url, connection, handler, entity_set, entity_key, method="PAT self._method = method.upper() if self._method not in EntityModifyRequest.ALLOWED_HTTP_METHODS: - raise ValueError('The value "{}" is not on the list of allowed Entity Update HTTP Methods: {}' - .format(method, ', '.join(EntityModifyRequest.ALLOWED_HTTP_METHODS))) + raise ValueError( + 'The value "{}" is not on the list of allowed Entity Update HTTP Methods: {}'.format( + method, ", ".join(EntityModifyRequest.ALLOWED_HTTP_METHODS) + ) + ) self._values = {} # get all properties declared by entity type self._type_props = self._entity_type.proprties() - self._logger.debug('New instance of EntityModifyRequest for entity type: %s', self._entity_type.name) + self._logger.debug( + "New instance of EntityModifyRequest for entity type: %s", + self._entity_type.name, + ) def get_path(self): return self._entity_set.name + self._entity_key.to_key_string() @@ -555,7 +619,11 @@ def get_body(self): return json.dumps(body) def get_headers(self): - return {'Accept': 'application/json', 'Content-Type': 'application/json', **self._headers} + return { + "Accept": "application/json", + "Content-Type": "application/json", + **self._headers, + } def set(self, **kwargs): """Set properties to be changed.""" @@ -567,7 +635,10 @@ def set(self, **kwargs): val = self._entity_type.proprty(key).to_json(val) except KeyError: raise PyODataException( - 'Property {} is not declared in {} entity type'.format(key, self._entity_type.name)) + "Property {} is not declared in {} entity type".format( + key, self._entity_type.name + ) + ) self._values[key] = val @@ -592,7 +663,9 @@ def __init__(self, url, connection, handler, last_segment): self._expand = None self._last_segment = last_segment self._customs = {} # string -> string hash - self._logger.debug('New instance of QueryRequest for last segment: %s', self._last_segment) + self._logger.debug( + "New instance of QueryRequest for last segment: %s", self._last_segment + ) def custom(self, name, value): """Adds a custom name-value pair.""" @@ -643,7 +716,7 @@ def top(self, top): def get_path(self): if self._count: - return urljoin(self._last_segment, '/$count') + return urljoin(self._last_segment, "/$count") return self._last_segment @@ -651,34 +724,31 @@ def get_headers(self): if self._count: return {} - return { - 'Accept': 'application/json', - **self._headers - } + return {"Accept": "application/json", **self._headers} def get_query_params(self): qparams = super(QueryRequest, self).get_query_params() if self._top is not None: - qparams['$top'] = self._top + qparams["$top"] = self._top if self._skip is not None: - qparams['$skip'] = self._skip + qparams["$skip"] = self._skip if self._order_by is not None: - qparams['$orderby'] = self._order_by + qparams["$orderby"] = self._order_by if self._filter is not None: - qparams['$filter'] = self._filter + qparams["$filter"] = self._filter if self._select is not None: - qparams['$select'] = self._select + qparams["$select"] = self._select for key, val in self._customs.items(): qparams[key] = val if self._expand is not None: - qparams['$expand'] = self._expand + qparams["$expand"] = self._expand return qparams @@ -687,14 +757,18 @@ class FunctionRequest(QueryRequest): """Function import request (Service call)""" def __init__(self, url, connection, handler, function_import): - super(FunctionRequest, self).__init__(url, connection, handler, function_import.name) + super(FunctionRequest, self).__init__( + url, connection, handler, function_import.name + ) self._function_import = function_import - self._logger.debug('New instance of FunctionRequest for %s', self._function_import.name) + self._logger.debug( + "New instance of FunctionRequest for %s", self._function_import.name + ) def parameter(self, name, value): - '''Sets value of parameter.''' + """Sets value of parameter.""" # check if param is valid (is declared in metadata) try: @@ -703,8 +777,11 @@ def parameter(self, name, value): # add parameter as custom query argument self.custom(param.name, param.to_literal(value)) except KeyError: - raise PyODataException('Function import {0} does not have pararmeter {1}' - .format(self._function_import.name, name)) + raise PyODataException( + "Function import {0} does not have pararmeter {1}".format( + self._function_import.name, name + ) + ) return self @@ -712,10 +789,7 @@ def get_method(self): return self._function_import.http_method def get_headers(self): - return { - 'Accept': 'application/json', - **self._headers - } + return {"Accept": "application/json", **self._headers} class EntityProxy: @@ -726,7 +800,9 @@ class EntityProxy: # pylint: disable=too-many-branches,too-many-nested-blocks - def __init__(self, service, entity_set, entity_type, proprties=None, entity_key=None): + def __init__( + self, service, entity_set, entity_type, proprties=None, entity_key=None + ): self._logger = logging.getLogger(LOGGER_NAME) self._service = service self._entity_set = entity_set @@ -735,7 +811,11 @@ def __init__(self, service, entity_set, entity_type, proprties=None, entity_key= self._cache = dict() self._entity_key = entity_key - self._logger.debug('New entity proxy instance of type %s from properties: %s', entity_type.name, proprties) + self._logger.debug( + "New entity proxy instance of type %s from properties: %s", + entity_type.name, + proprties, + ) # cache values of individual properties if provided if proprties is not None: @@ -744,10 +824,14 @@ def __init__(self, service, entity_set, entity_type, proprties=None, entity_key= for type_proprty in self._entity_type.proprties(): if type_proprty.name in proprties: if proprties[type_proprty.name] is not None: - self._cache[type_proprty.name] = type_proprty.from_json(proprties[type_proprty.name]) + self._cache[type_proprty.name] = type_proprty.from_json( + proprties[type_proprty.name] + ) else: # null value is in literal form for now, convert it to python representation - self._cache[type_proprty.name] = type_proprty.from_literal(type_proprty.typ.null_value) + self._cache[type_proprty.name] = type_proprty.from_literal( + type_proprty.typ.null_value + ) # then, assign all navigation properties for prop in self._entity_type.nav_proprties: @@ -758,30 +842,41 @@ def __init__(self, service, entity_set, entity_type, proprties=None, entity_key= prop_etype = prop.to_role.entity_type # cache value according to multiplicity - if prop.to_role.multiplicity in \ - [model.EndRole.MULTIPLICITY_ONE, - model.EndRole.MULTIPLICITY_ZERO_OR_ONE]: + if prop.to_role.multiplicity in [ + model.EndRole.MULTIPLICITY_ONE, + model.EndRole.MULTIPLICITY_ZERO_OR_ONE, + ]: # cache None in case we receive nothing (null) instead of entity data if proprties[prop.name] is None: self._cache[prop.name] = None else: - self._cache[prop.name] = EntityProxy(service, None, prop_etype, proprties[prop.name]) - - elif prop.to_role.multiplicity == model.EndRole.MULTIPLICITY_ZERO_OR_MORE: + self._cache[prop.name] = EntityProxy( + service, None, prop_etype, proprties[prop.name] + ) + + elif ( + prop.to_role.multiplicity + == model.EndRole.MULTIPLICITY_ZERO_OR_MORE + ): # default value is empty array self._cache[prop.name] = [] # if there are no entities available, received data consists of # metadata properties only. - if 'results' in proprties[prop.name]: + if "results" in proprties[prop.name]: # available entities are serialized in results array - for entity in proprties[prop.name]['results']: - self._cache[prop.name].append(EntityProxy(service, None, prop_etype, entity)) + for entity in proprties[prop.name]["results"]: + self._cache[prop.name].append( + EntityProxy(service, None, prop_etype, entity) + ) else: - raise PyODataException('Unknown multiplicity {0} of association role {1}' - .format(prop.to_role.multiplicity, prop.to_role.name)) + raise PyODataException( + "Unknown multiplicity {0} of association role {1}".format( + prop.to_role.multiplicity, prop.to_role.name + ) + ) # build entity key if not provided if self._entity_key is None: @@ -789,7 +884,9 @@ def __init__(self, service, entity_set, entity_type, proprties=None, entity_key= try: # if key seems to be simple (consists of single property) if len(self._key_props) == 1: - self._entity_key = EntityKey(entity_type, self._cache[self._key_props[0].name]) + self._entity_key = EntityKey( + entity_type, self._cache[self._key_props[0].name] + ) else: # build complex key self._entity_key = EntityKey(entity_type, **self._cache) @@ -810,8 +907,11 @@ def __getattr__(self, attr): self._cache[attr] = value return value except KeyError as ex: - raise AttributeError('EntityType {0} does not have Property {1}: {2}' - .format(self._entity_type.name, attr, str(ex))) + raise AttributeError( + "EntityType {0} does not have Property {1}: {2}".format( + self._entity_type.name, attr, str(ex) + ) + ) def nav(self, nav_property): """Navigates to given navigation property and returns the EntitySetProxy""" @@ -820,58 +920,83 @@ def nav(self, nav_property): try: navigation_property = self._entity_type.nav_proprty(nav_property) except KeyError: - raise PyODataException('Navigation property {} is not declared in {} entity type'.format( - nav_property, self._entity_type)) + raise PyODataException( + "Navigation property {} is not declared in {} entity type".format( + nav_property, self._entity_type + ) + ) # Get entity set of navigation property association_info = navigation_property.association_info association_set = self._service.schema.association_set_by_association( - association_info.name, - association_info.namespace) + association_info.name, association_info.namespace + ) navigation_entity_set = None for end in association_set.end_roles: - if association_set.end_by_entity_set(end.entity_set_name).role == navigation_property.to_role.role: - navigation_entity_set = self._service.schema.entity_set(end.entity_set_name, association_info.namespace) + if ( + association_set.end_by_entity_set(end.entity_set_name).role + == navigation_property.to_role.role + ): + navigation_entity_set = self._service.schema.entity_set( + end.entity_set_name, association_info.namespace + ) if not navigation_entity_set: - raise PyODataException('No association set for role {}'.format(navigation_property.to_role)) + raise PyODataException( + "No association set for role {}".format(navigation_property.to_role) + ) roles = navigation_property.association.end_roles - if all((role.multiplicity != model.EndRole.MULTIPLICITY_ZERO_OR_MORE for role in roles)): - return NavEntityProxy(self, nav_property, navigation_entity_set.entity_type, {}) + if all( + ( + role.multiplicity != model.EndRole.MULTIPLICITY_ZERO_OR_MORE + for role in roles + ) + ): + return NavEntityProxy( + self, nav_property, navigation_entity_set.entity_type, {} + ) return EntitySetProxy( self._service, self._service.schema.entity_set(navigation_entity_set.name), nav_property, - self._entity_set.name + self._entity_key.to_key_string()) + self._entity_set.name + self._entity_key.to_key_string(), + ) def get_path(self): """Returns this entity's relative path - e.g. EntitySet(KEY)""" - return self._entity_set._name + self._entity_key.to_key_string() # pylint: disable=protected-access + return ( + self._entity_set._name + self._entity_key.to_key_string() + ) # pylint: disable=protected-access def get_proprty(self, name, connection=None): """Returns value of the property""" - self._logger.info('Initiating property request for %s', name) + self._logger.info("Initiating property request for %s", name) def proprty_get_handler(key, proprty, response): """Gets property value from HTTP Response""" if response.status_code != HTTP_CODE_OK: - raise HttpError('HTTP GET for Attribute {0} of Entity {1} failed with status code {2}' - .format(proprty.name, key, response.status_code), response) + raise HttpError( + "HTTP GET for Attribute {0} of Entity {1} failed with status code {2}".format( + proprty.name, key, response.status_code + ), + response, + ) - data = response.json()['d'] + data = response.json()["d"] return proprty.from_json(data[proprty.name]) path = urljoin(self.get_path(), name) return self._service.http_get_odata( path, partial(proprty_get_handler, path, self._entity_type.proprty(name)), - connection=connection) + connection=connection, + ) def get_value(self, connection=None): "Returns $value of Stream entities" @@ -880,15 +1005,19 @@ def value_get_handler(key, response): """Gets property value from HTTP Response""" if response.status_code != HTTP_CODE_OK: - raise HttpError('HTTP GET for $value of Entity {0} failed with status code {1}' - .format(key, response.status_code), response) + raise HttpError( + "HTTP GET for $value of Entity {0} failed with status code {1}".format( + key, response.status_code + ), + response, + ) return response - path = urljoin(self.get_path(), '/$value') - return self._service.http_get_odata(path, - partial(value_get_handler, self.entity_key), - connection=connection) + path = urljoin(self.get_path(), "/$value") + return self._service.http_get_odata( + path, partial(value_get_handler, self.entity_key), connection=connection + ) @property def entity_set(self): @@ -906,7 +1035,7 @@ def entity_key(self): def url(self): """URL of the real entity""" - service_url = self._service.url.rstrip('/') + service_url = self._service.url.rstrip("/") entity_path = self.get_path() return urljoin(service_url, entity_path) @@ -922,7 +1051,9 @@ class NavEntityProxy(EntityProxy): def __init__(self, parent_entity, prop_name, entity_type, entity): # pylint: disable=protected-access - super(NavEntityProxy, self).__init__(parent_entity._service, parent_entity._entity_set, entity_type, entity) + super(NavEntityProxy, self).__init__( + parent_entity._service, parent_entity._entity_set, entity_type, entity + ) self._parent_entity = parent_entity self._prop_name = prop_name @@ -944,47 +1075,52 @@ def build_expression(operator, operands): """Creates a expression by joining the operands with the operator""" if len(operands) < 2: - raise ExpressionError('The $filter operator \'{}\' needs at least two operands'.format(operator)) + raise ExpressionError( + "The $filter operator '{}' needs at least two operands".format(operator) + ) - return '({})'.format(' {} '.format(operator).join(operands)) + return "({})".format(" {} ".format(operator).join(operands)) @staticmethod def and_(*operands): """Creates logical AND expression from the operands""" - return GetEntitySetFilter.build_expression('and', operands) + return GetEntitySetFilter.build_expression("and", operands) @staticmethod def or_(*operands): """Creates logical OR expression from the operands""" - return GetEntitySetFilter.build_expression('or', operands) + return GetEntitySetFilter.build_expression("or", operands) @staticmethod def format_filter(proprty, operator, value): """Creates a filter expression """ - return '{} {} {}'.format(proprty.name, operator, proprty.to_literal(value)) + return "{} {} {}".format(proprty.name, operator, proprty.to_literal(value)) def __eq__(self, value): - return GetEntitySetFilter.format_filter(self._proprty, 'eq', value) + return GetEntitySetFilter.format_filter(self._proprty, "eq", value) def __ne__(self, value): - return GetEntitySetFilter.format_filter(self._proprty, 'ne', value) + return GetEntitySetFilter.format_filter(self._proprty, "ne", value) def __lt__(self, value): - return GetEntitySetFilter.format_filter(self._proprty, 'lt', value) + return GetEntitySetFilter.format_filter(self._proprty, "lt", value) def __le__(self, value): - return GetEntitySetFilter.format_filter(self._proprty, 'le', value) + return GetEntitySetFilter.format_filter(self._proprty, "le", value) def __ge__(self, value): - return GetEntitySetFilter.format_filter(self._proprty, 'ge', value) + return GetEntitySetFilter.format_filter(self._proprty, "ge", value) + def __gt__(self, value): - return GetEntitySetFilter.format_filter(self._proprty, 'gt', value) + return GetEntitySetFilter.format_filter(self._proprty, "gt", value) -class FilterExpression(object): - def __init__(self, *args, **kwargs): + +class FilterExpression: + """A filter expression object comparable to Django's Q class""" + def __init__(self, **kwargs): self.expressions = kwargs self.other = None self.operator = None @@ -999,7 +1135,8 @@ def __and__(self, other): self.operator = "and" return self -class GetEntitySetFilterChainable(object): + +class GetEntitySetFilterChainable: """ Example expressions FirstName="Tim" @@ -1015,36 +1152,54 @@ class GetEntitySetFilterChainable(object): Addresses__Suburb="Chatswood" Addresses__Suburb__contains="wood" """ - operators = ["startswith", "endswith", "lt", "lte", "gt", "gte", "contains", "range", "in", "length"] + + operators = [ + "startswith", + "endswith", + "lt", + "lte", + "gt", + "gte", + "contains", + "range", + "in", + "length", + ] + def __init__(self, request, filter_expressions, exprs): self.request = request self.expressions = exprs self.filter_expressions = filter_expressions def proprty_obj(self, name): - return self.request._entity_type.proprty(name) + """Returns value for a particular proprty""" + return self.request._entity_type.proprty(name) # pylint: disable=protected-access def process_query_objects(self): + """Processes FilterExpression objects to OData lookups""" filter_expressions = [] - for q in self.filter_expressions: + for filter_expression in self.filter_expressions: lhs_expressions = [] rhs_expressions = [] - for expr, val in q.expressions.items(): + for expr, val in filter_expression.expressions.items(): lhs_expressions.append(self.decode_expression(expr, val)) - lhs_expressions = self.combine_expressions(lhs_expressions) + lhs_expression = self.combine_expressions(lhs_expressions) - if q.other: - for expr, val in q.other.expressions.items(): + if filter_expression.other: + for expr, val in filter_expression.other.expressions.items(): rhs_expressions.append(self.decode_expression(expr, val)) - rhs_expressions = self.combine_expressions(rhs_expressions) - - filter_expressions.append(f"({lhs_expressions}) {q.operator} ({rhs_expressions})") + rhs_expression = self.combine_expressions(rhs_expressions) + + filter_expressions.append( + f"({lhs_expression}) {filter_expression.operator} ({rhs_expression})" + ) else: filter_expressions.append(lhs_expression) return filter_expressions def process_expressions(self): + """Processes filter kwargs into OData expressions""" filter_expressions = [] for expr, val in self.expressions.items(): filter_expressions.append(self.decode_expression(expr, val)) @@ -1053,7 +1208,8 @@ def process_expressions(self): return filter_expressions def decode_expression(self, expr, val): - properties = self.request._entity_type._properties.keys() + """Decodes Django-like syntax into OData expressions""" + properties = self.request._entity_type._properties.keys() # pylint: disable=protected-access field = None # field_heirarchy = [] operator = "eq" @@ -1070,13 +1226,17 @@ def decode_expression(self, expr, val): # target_field = self.proprty_obj(field_heirarchy[-1]) expression = self.build_expression(field, operator, val) - + return expression - + def combine_expressions(self, expressions): + """Combines expressions""" + # pylint: disable=no-self-use return " and ".join(expressions) def build_expression(self, field_name, operator, value): + """Builds expression from Django-like operator""" + # pylint: disable=too-many-branches, too-many-return-statements, no-else-return target_field = self.proprty_obj(field_name) if operator not in ["length", "in", "range"]: value = target_field.to_literal(value) @@ -1098,18 +1258,20 @@ def build_expression(self, field_name, operator, value): elif operator in ["contains"]: return f"substringof({value}, {field_name}) eq true" elif operator == "range": - if not (isinstance(value, tuple) or isinstance(value, list)): - raise TypeError("Range must be tuple or list not {}".format(type(value))) + if not isinstance(value, (tuple, list)): + raise TypeError( + "Range must be tuple or list not {}".format(type(value)) + ) if len(value) != 2: raise ValueError("Only two items can be passed in a range.") - - x = target_field.to_literal(value[0]) - y = target_field.to_literal(value[1]) - return f"{field_name} gte {x} and {field_name} lte {y}" + + value_0 = target_field.to_literal(value[0]) + value_1 = target_field.to_literal(value[1]) + return f"{field_name} gte {value_0} and {field_name} lte {value_1}" elif operator == "in": literal_values = [] - for v in value: - val = target_field.to_literal(v) + for val in value: + val = target_field.to_literal(val) literal_values.append(f"{field_name} eq {val}") return " or ".join(literal_values) elif operator == "eq": @@ -1118,15 +1280,19 @@ def build_expression(self, field_name, operator, value): raise ValueError(f"Invalid expression {operator}") def as_filter_string(self): + """Returns final filter string for this filter""" expressions = self.process_expressions() result = self.combine_expressions(expressions) return quote(result) + class GetEntitySetRequest(QueryRequest): """GET on EntitySet""" def __init__(self, url, connection, handler, last_segment, entity_type): - super(GetEntitySetRequest, self).__init__(url, connection, handler, last_segment) + super(GetEntitySetRequest, self).__init__( + url, connection, handler, last_segment + ) self._entity_type = entity_type @@ -1135,16 +1301,20 @@ def __getattr__(self, name): return GetEntitySetFilter(proprty) def set_filter(self, filter_val): + """Chain filter""" filter_text = self._filter + " and " if self._filter else "" filter_text += filter_val self._filter = filter_text def filter(self, *args, **kwargs): - if len(args) and isinstance(args[0], str): + # pylint: disable=no-else-return + if args and isinstance(args[0], str): self._filter = args[0] return self else: - self.set_filter(GetEntitySetFilterChainable(self, args, kwargs).as_filter_string()) + self.set_filter( + GetEntitySetFilterChainable(self, args, kwargs).as_filter_string() + ) return self @@ -1161,17 +1331,17 @@ def __init__(self, service, entity_set, alias=None, parent_last_segment=None): self._entity_set = entity_set self._alias = alias if parent_last_segment is None: - self._parent_last_segment = '' + self._parent_last_segment = "" else: - if parent_last_segment.endswith('/'): + if parent_last_segment.endswith("/"): self._parent_last_segment = parent_last_segment else: - self._parent_last_segment = parent_last_segment + '/' + self._parent_last_segment = parent_last_segment + "/" self._name = entity_set.name self._key = entity_set.entity_type.key_proprties self._logger = logging.getLogger(LOGGER_NAME) - self._logger.debug('New entity set proxy instance for %s', self._name) + self._logger.debug("New entity set proxy instance for %s", self._name) @property def service(self): @@ -1182,7 +1352,9 @@ def service(self): def last_segment(self): """Return last segment of url""" - entity_set_name = self._alias if self._alias is not None else self._entity_set.name + entity_set_name = ( + self._alias if self._alias is not None else self._entity_set.name + ) return self._parent_last_segment + entity_set_name def nav(self, nav_property, key): @@ -1191,32 +1363,50 @@ def nav(self, nav_property, key): try: navigation_property = self._entity_set.entity_type.nav_proprty(nav_property) except KeyError: - raise PyODataException('Navigation property {} is not declared in {} entity type'.format( - nav_property, self._entity_set.entity_type)) + raise PyODataException( + "Navigation property {} is not declared in {} entity type".format( + nav_property, self._entity_set.entity_type + ) + ) # Get entity set of navigation property association_info = navigation_property.association_info association_set = self._service.schema.association_set_by_association( - association_info.name) + association_info.name + ) navigation_entity_set = None for end in association_set.end_roles: - if association_set.end_by_entity_set(end.entity_set_name).role == navigation_property.to_role.role: - navigation_entity_set = self._service.schema.entity_set(end.entity_set_name) + if ( + association_set.end_by_entity_set(end.entity_set_name).role + == navigation_property.to_role.role + ): + navigation_entity_set = self._service.schema.entity_set( + end.entity_set_name + ) if not navigation_entity_set: raise PyODataException( - 'No association set for role {} {}'.format(navigation_property.to_role, association_set.end_roles)) + "No association set for role {} {}".format( + navigation_property.to_role, association_set.end_roles + ) + ) roles = navigation_property.association.end_roles - if all((role.multiplicity != model.EndRole.MULTIPLICITY_ZERO_OR_MORE for role in roles)): + if all( + ( + role.multiplicity != model.EndRole.MULTIPLICITY_ZERO_OR_MORE + for role in roles + ) + ): return self._get_nav_entity(key, nav_property, navigation_entity_set) return EntitySetProxy( self._service, navigation_entity_set, nav_property, - self._entity_set.name + key.to_key_string()) + self._entity_set.name + key.to_key_string(), + ) def _get_nav_entity(self, master_key, nav_property, navigation_entity_set): """Get entity based on provided key of the master and Navigation property name""" @@ -1225,26 +1415,36 @@ def get_entity_handler(parent, nav_property, navigation_entity_set, response): """Gets entity from HTTP response""" if response.status_code != HTTP_CODE_OK: - raise HttpError('HTTP GET for Entity {0} failed with status code {1}' - .format(self._name, response.status_code), response) + raise HttpError( + "HTTP GET for Entity {0} failed with status code {1}".format( + self._name, response.status_code + ), + response, + ) - entity = response.json()['d'] + entity = response.json()["d"] - return NavEntityProxy(parent, nav_property, navigation_entity_set.entity_type, entity) + return NavEntityProxy( + parent, nav_property, navigation_entity_set.entity_type, entity + ) self._logger.info( - 'Getting the nav property %s of the entity %s for the key %s', + "Getting the nav property %s of the entity %s for the key %s", nav_property, self._entity_set.entity_type.name, - master_key) + master_key, + ) - parent = EntityProxy(self._service, self, self._entity_set.entity_type, entity_key=master_key) + parent = EntityProxy( + self._service, self, self._entity_set.entity_type, entity_key=master_key + ) return NavEntityGetRequest( partial(get_entity_handler, parent, nav_property, navigation_entity_set), master_key, self, - nav_property) + nav_property, + ) def get_entity(self, key=None, **args): """Get entity based on provided key properties""" @@ -1253,19 +1453,30 @@ def get_entity_handler(response): """Gets entity from HTTP response""" if response.status_code != HTTP_CODE_OK: - raise HttpError('HTTP GET for Entity {0} failed with status code {1}' - .format(self._name, response.status_code), response) + raise HttpError( + "HTTP GET for Entity {0} failed with status code {1}".format( + self._name, response.status_code + ), + response, + ) - entity = response.json()['d'] + entity = response.json()["d"] - return EntityProxy(self._service, self._entity_set, self._entity_set.entity_type, entity) + return EntityProxy( + self._service, self._entity_set, self._entity_set.entity_type, entity + ) if key is not None and isinstance(key, EntityKey): entity_key = key else: entity_key = EntityKey(self._entity_set.entity_type, key, **args) - self._logger.info('Getting entity %s for key %s and args %s', self._entity_set.entity_type.name, key, args) + self._logger.info( + "Getting entity %s for key %s and args %s", + self._entity_set.entity_type.name, + key, + args, + ) return EntityGetRequest(get_entity_handler, entity_key, self) @@ -1276,30 +1487,43 @@ def get_entities_handler(response): """Gets entity set from HTTP Response""" if response.status_code != HTTP_CODE_OK: - raise HttpError('HTTP GET for Entity Set {0} failed with status code {1}' - .format(self._name, response.status_code), response) + raise HttpError( + "HTTP GET for Entity Set {0} failed with status code {1}".format( + self._name, response.status_code + ), + response, + ) content = response.json() if isinstance(content, int): return content - entities = content['d'] + entities = content["d"] if isinstance(entities, dict): - entities = entities['results'] + entities = entities["results"] - self._logger.info('Fetched %d entities', len(entities)) + self._logger.info("Fetched %d entities", len(entities)) result = [] for props in entities: - entity = EntityProxy(self._service, self._entity_set, self._entity_set.entity_type, props) + entity = EntityProxy( + self._service, self._entity_set, self._entity_set.entity_type, props + ) result.append(entity) return result - entity_set_name = self._alias if self._alias is not None else self._entity_set.name - return GetEntitySetRequest(self._service.url, self._service.connection, get_entities_handler, - self._parent_last_segment + entity_set_name, self._entity_set.entity_type) + entity_set_name = ( + self._alias if self._alias is not None else self._entity_set.name + ) + return GetEntitySetRequest( + self._service.url, + self._service.connection, + get_entities_handler, + self._parent_last_segment + entity_set_name, + self._entity_set.entity_type, + ) def create_entity(self, return_code=HTTP_CODE_CREATED): """Creates a new entity in the given entity-set.""" @@ -1308,15 +1532,29 @@ def create_entity_handler(response): """Gets newly created entity encoded in HTTP Response""" if response.status_code != return_code: - raise HttpError('HTTP POST for Entity Set {0} failed with status code {1}' - .format(self._name, response.status_code), response) - - entity_props = response.json()['d'] - - return EntityProxy(self._service, self._entity_set, self._entity_set.entity_type, entity_props) - - return EntityCreateRequest(self._service.url, self._service.connection, create_entity_handler, self._entity_set, - self.last_segment) + raise HttpError( + "HTTP POST for Entity Set {0} failed with status code {1}".format( + self._name, response.status_code + ), + response, + ) + + entity_props = response.json()["d"] + + return EntityProxy( + self._service, + self._entity_set, + self._entity_set.entity_type, + entity_props, + ) + + return EntityCreateRequest( + self._service.url, + self._service.connection, + create_entity_handler, + self._entity_set, + self.last_segment, + ) def update_entity(self, key=None, method=None, **kwargs): """Updates an existing entity in the given entity-set.""" @@ -1325,21 +1563,36 @@ def update_entity_handler(response): """Gets modified entity encoded in HTTP Response""" if response.status_code != 204: - raise HttpError('HTTP modify request for Entity Set {} failed with status code {}' - .format(self._name, response.status_code), response) + raise HttpError( + "HTTP modify request for Entity Set {} failed with status code {}".format( + self._name, response.status_code + ), + response, + ) if key is not None and isinstance(key, EntityKey): entity_key = key else: entity_key = EntityKey(self._entity_set.entity_type, key, **kwargs) - self._logger.info('Updating entity %s for key %s and args %s', self._entity_set.entity_type.name, key, kwargs) + self._logger.info( + "Updating entity %s for key %s and args %s", + self._entity_set.entity_type.name, + key, + kwargs, + ) if method is None: - method = self._service.config['http']['update_method'] - - return EntityModifyRequest(self._service.url, self._service.connection, update_entity_handler, self._entity_set, - entity_key, method=method) + method = self._service.config["http"]["update_method"] + + return EntityModifyRequest( + self._service.url, + self._service.connection, + update_entity_handler, + self._entity_set, + entity_key, + method=method, + ) def delete_entity(self, key: EntityKey = None, **kwargs): """Delete the entity""" @@ -1348,17 +1601,24 @@ def delete_entity_handler(response): """Check if entity deletion was successful""" if response.status_code != 204: - raise HttpError(f'HTTP POST for Entity delete {self._name} ' - f'failed with status code {response.status_code}', - response) + raise HttpError( + f"HTTP POST for Entity delete {self._name} " + f"failed with status code {response.status_code}", + response, + ) if key is not None and isinstance(key, EntityKey): entity_key = key else: entity_key = EntityKey(self._entity_set.entity_type, key, **kwargs) - return EntityDeleteRequest(self._service.url, self._service.connection, delete_entity_handler, self._entity_set, - entity_key) + return EntityDeleteRequest( + self._service.url, + self._service.connection, + delete_entity_handler, + self._entity_set, + entity_key, + ) # pylint: disable=too-few-public-methods @@ -1371,14 +1631,19 @@ def __init__(self, service): self._entity_sets = dict() for entity_set in self._service.schema.entity_sets: - self._entity_sets[entity_set.name] = EntitySetProxy(self._service, entity_set) + self._entity_sets[entity_set.name] = EntitySetProxy( + self._service, entity_set + ) def __getattr__(self, name): try: return self._entity_sets[name] except KeyError: raise AttributeError( - 'EntitySet {0} not defined in {1}.'.format(name, ','.join(list(self._entity_sets.keys())))) + "EntitySet {0} not defined in {1}.".format( + name, ",".join(list(self._entity_sets.keys())) + ) + ) class FunctionContainer: @@ -1399,7 +1664,10 @@ def __getattr__(self, name): if name not in self._functions: raise AttributeError( - 'Function {0} not defined in {1}.'.format(name, ','.join(list(self._functions.keys())))) + "Function {0} not defined in {1}.".format( + name, ",".join(list(self._functions.keys())) + ) + ) fimport = self._service.schema.function_import(name) @@ -1407,60 +1675,82 @@ def function_import_handler(fimport, response): """Get function call response from HTTP Response""" if 300 <= response.status_code < 400: - raise HttpError(f'Function Import {fimport.name} requires Redirection which is not supported', - response) + raise HttpError( + f"Function Import {fimport.name} requires Redirection which is not supported", + response, + ) if response.status_code == 401: - raise HttpError(f'Not authorized to call Function Import {fimport.name}', - response) + raise HttpError( + f"Not authorized to call Function Import {fimport.name}", response + ) if response.status_code == 403: - raise HttpError(f'Missing privileges to call Function Import {fimport.name}', - response) + raise HttpError( + f"Missing privileges to call Function Import {fimport.name}", + response, + ) if response.status_code == 405: raise HttpError( - f'Despite definition Function Import {fimport.name} does not support HTTP {fimport.http_method}', - response) + f"Despite definition Function Import {fimport.name} does not support HTTP {fimport.http_method}", + response, + ) if 400 <= response.status_code < 500: raise HttpError( - f'Function Import {fimport.name} call has failed with status code {response.status_code}', - response) + f"Function Import {fimport.name} call has failed with status code {response.status_code}", + response, + ) if response.status_code >= 500: - raise HttpError(f'Server has encountered an error while processing Function Import {fimport.name}', - response) + raise HttpError( + f"Server has encountered an error while processing Function Import {fimport.name}", + response, + ) if fimport.return_type is None: if response.status_code != 204: logging.getLogger(LOGGER_NAME).warning( - 'The No Return Function Import %s has replied with HTTP Status Code %d instead of 204', - fimport.name, response.status_code) + "The No Return Function Import %s has replied with HTTP Status Code %d instead of 204", + fimport.name, + response.status_code, + ) if response.text: logging.getLogger(LOGGER_NAME).warning( - 'The No Return Function Import %s has returned content:\n%s', fimport.name, response.text) + "The No Return Function Import %s has returned content:\n%s", + fimport.name, + response.text, + ) return None if response.status_code != 200: logging.getLogger(LOGGER_NAME).warning( - 'The Function Import %s has replied with HTTP Status Code %d instead of 200', - fimport.name, response.status_code) + "The Function Import %s has replied with HTTP Status Code %d instead of 200", + fimport.name, + response.status_code, + ) - response_data = response.json()['d'] + response_data = response.json()["d"] # 1. if return types is "entity type", return instance of appropriate entity proxy if isinstance(fimport.return_type, model.EntityType): entity_set = self._service.schema.entity_set(fimport.entity_set_name) - return EntityProxy(self._service, entity_set, fimport.return_type, response_data) + return EntityProxy( + self._service, entity_set, fimport.return_type, response_data + ) # 2. return raw data for all other return types (primitives, complex types encoded in dicts, etc.) return response_data - return FunctionRequest(self._service.url, self._service.connection, - partial(function_import_handler, fimport), fimport) + return FunctionRequest( + self._service.url, + self._service.connection, + partial(function_import_handler, fimport), + fimport, + ) class Service: @@ -1473,7 +1763,7 @@ def __init__(self, url, schema, connection): self._entity_container = EntityContainer(self) self._function_container = FunctionContainer(self) - self._config = {'http': {'update_method': 'PATCH'}} + self._config = {"http": {"update_method": "PATCH"}} @property def schema(self): @@ -1531,7 +1821,8 @@ def http_get_odata(self, path, handler, connection=None): urljoin(self._url, path), conn, handler, - headers={'Accept': 'application/json'}) + headers={"Accept": "application/json"}, + ) def create_batch(self, batch_id=None): """Create instance of OData batch request""" @@ -1539,11 +1830,15 @@ def create_batch(self, batch_id=None): def batch_handler(batch, parts): """Process parsed multipart request (parts)""" - logging.getLogger(LOGGER_NAME).debug('Batch handler called for batch %s', batch.id) + logging.getLogger(LOGGER_NAME).debug( + "Batch handler called for batch %s", batch.id + ) result = [] for part, req in zip(parts, batch.requests): - logging.getLogger(LOGGER_NAME).debug('Batch handler is processing part %s for request %s', part, req) + logging.getLogger(LOGGER_NAME).debug( + "Batch handler is processing part %s for request %s", part, req + ) # if part represents multiple requests, dont' parse body and # process parts by appropriate reuqest instance @@ -1564,7 +1859,9 @@ def create_changeset(self, changeset_id=None): def changeset_handler(changeset, parts): """Gets changeset response from HTTP response""" - logging.getLogger(LOGGER_NAME).debug('Changeset handler called for changeset %s', changeset.id) + logging.getLogger(LOGGER_NAME).debug( + "Changeset handler called for changeset %s", changeset.id + ) result = [] @@ -1575,15 +1872,22 @@ def changeset_handler(changeset, parts): # raise error (even for successfull status codes) since such changeset response # always means something wrong happened on server response = ODataHttpResponse.from_string(parts[0]) - raise HttpError('Changeset cannot be processed due to single response received, status code: {}'.format( - response.status_code), response) + raise HttpError( + "Changeset cannot be processed due to single response received, status code: {}".format( + response.status_code + ), + response, + ) for part, req in zip(parts, changeset.requests): - logging.getLogger(LOGGER_NAME).debug('Changeset handler is processing part %s for request %s', part, - req) + logging.getLogger(LOGGER_NAME).debug( + "Changeset handler is processing part %s for request %s", part, req + ) if isinstance(req, MultipartRequest): - raise PyODataException('Changeset cannot contain nested multipart content') + raise PyODataException( + "Changeset cannot contain nested multipart content" + ) # part represents single request, we have to parse # content (without checking Content type for binary/http) @@ -1600,17 +1904,30 @@ class MultipartRequest(ODataHttpRequest): """HTTP Batch request""" def __init__(self, url, connection, handler, request_id=None): - super(MultipartRequest, self).__init__(url, connection, partial(MultipartRequest.http_response_handler, self)) + super(MultipartRequest, self).__init__( + url, connection, partial(MultipartRequest.http_response_handler, self) + ) self.requests = [] self._handler_decoded = handler # generate random id of form dddd-dddd-dddd # pylint: disable=invalid-name - self.id = request_id if request_id is not None else '{}_{}_{}'.format( - random.randint(1000, 9999), random.randint(1000, 9999), random.randint(1000, 9999)) + self.id = ( + request_id + if request_id is not None + else "{}_{}_{}".format( + random.randint(1000, 9999), + random.randint(1000, 9999), + random.randint(1000, 9999), + ) + ) - self._logger.debug('New multipart %s request initialized, id=%s', self.__class__.__name__, self.id) + self._logger.debug( + "New multipart %s request initialized, id=%s", + self.__class__.__name__, + self.id, + ) @property def handler(self): @@ -1622,7 +1939,10 @@ def get_boundary(self): def get_headers(self): # pylint: disable=no-self-use - return {'Content-Type': 'multipart/mixed;boundary={}'.format(self.get_boundary()), **self._headers} + return { + "Content-Type": "multipart/mixed;boundary={}".format(self.get_boundary()), + **self._headers, + } def get_body(self): return encode_multipart(self.get_boundary(), self.requests) @@ -1631,20 +1951,32 @@ def add_request(self, request): """Add request to be sent in batch""" self.requests.append(request) - self._logger.debug('New %s request added to multipart request %s', request.get_method(), self.id) + self._logger.debug( + "New %s request added to multipart request %s", + request.get_method(), + self.id, + ) @staticmethod def http_response_handler(request, response): """Process HTTP response to mutipart HTTP request""" if response.status_code != 202: # 202 Accepted - raise HttpError('HTTP POST for multipart request {0} failed with status code {1}' - .format(request.id, response.status_code), response) - - logging.getLogger(LOGGER_NAME).debug('Generic multipart http response request handler called') + raise HttpError( + "HTTP POST for multipart request {0} failed with status code {1}".format( + request.id, response.status_code + ), + response, + ) + + logging.getLogger(LOGGER_NAME).debug( + "Generic multipart http response request handler called" + ) # get list of all parts (headers + body) - decoded = decode_multipart(response.content.decode('utf-8'), response.headers['Content-Type']) + decoded = decode_multipart( + response.content.decode("utf-8"), response.headers["Content-Type"] + ) return request.handler(request, decoded) @@ -1653,19 +1985,20 @@ class BatchRequest(MultipartRequest): """HTTP Batch request""" def get_boundary(self): - return 'batch_' + self.id + return "batch_" + self.id def get_path(self): # pylint: disable=no-self-use - return '$batch' + return "$batch" def get_method(self): # pylint: disable=no-self-use - return 'POST' + return "POST" class Changeset(MultipartRequest): """Representation of changeset (unsorted group of requests)""" def get_boundary(self): - return 'changeset_' + self.id + return "changeset_" + self.id +