Home > front end >  Converting a multiline string to a dictionary when stringvalues are already separated by colons
Converting a multiline string to a dictionary when stringvalues are already separated by colons

Time:06-30

I work with a module that handles errors like this:

class APIError(BetfairError):
    """
    Exception raised if error is found.
    """

    def __init__(
        self,
        response: Optional[dict],
        method: str = None,
        params: dict = None,
        exception: Exception = None,
    ):
        super(APIError, self).__init__(response, method, params, exception)
        self.response = response
        self.method = method
        self.params = params
        self.exception = exception

    def __str__(self):
        if self.response:
            error_data = self.response.get("error")
            return "%s \nParams: %s \nException: %s \nError: %s \nFull Response: %s" % (
                self.method,
                self.params,
                self.exception,
                error_data,
                self.response,
            )
        else:
            return "%s \nParams: %s \nException: %s" % (
                self.method,
                self.params,
                self.exception,
            )

The error output has this multiline pattern of stringvalue already separated by a colon:

betfairlightweight.exceptions.APIError: SportsAPING/v1.0/listMarketBook 
Params: {'marketIds': ['1.200293211'], 'priceProjection': {'priceData': ['EX_TRADED'], 'exBestOffersOverrides': {}, 'virtualise': True, 'rolloverStakes': False}} 
Exception: None 
Error: {'code': -32099, 'message': 'ANGX-0006', 'data': {'APINGException': {'requestUUID': '...........', 'errorCode': 'UNEXPECTED_ERROR', 'errorDetails': ''}, 'exceptionname': 'APINGException'}} 
Full Response: {'jsonrpc': '2.0', 'error': {'code': -32099, 'message': 'ANGX-0006', 'data': {'APINGException': {'requestUUID': '..........', 'errorCode': 'UNEXPECTED_ERROR', 'errorDetails': ''}, 'exceptionname': 'APINGException'}}, 'id': 1}

So that I can access the values regardless of their position, I'm converting the string into a dictionary like this:


# except Exception as e:
#     e.__str__()

e = """betfairlightweight.exceptions.APIError: SportsAPING/v1.0/listMarketBook 
Params: {'marketIds': ['1.200293211'], 'priceProjection': {'priceData': ['EX_TRADED'], 'exBestOffersOverrides': {}, 'virtualise': True, 'rolloverStakes': False}} 
Exception: None 
Error: {'code': -32099, 'message': 'ANGX-0006', 'data': {'APINGException': {'requestUUID': '...........', 'errorCode': 'UNEXPECTED_ERROR', 'errorDetails': ''}, 'exceptionname': 'APINGException'}} 
Full Response: {'jsonrpc': '2.0', 'error': {'code': -32099, 'message': 'ANGX-0006', 'data': {'APINGException': {'requestUUID': '..........', 'errorCode': 'UNEXPECTED_ERROR', 'errorDetails': ''}, 'exceptionname': 'APINGException'}}, 'id': 1}"""

fs = []
nfs = []
for row in e.split('\n'):
    fs.append(row[:row.find(':')]   ':')
    nfs.append('\''   row[:row.find(':')]   '\''   ':')
for x, y in zip(fs, nfs):
    e = e.replace(x, y)
for z in e.split('\n'):
    if (': {' not in z) and (': None' not in z):
        e = e.replace(z, z.replace(': ', ': \'').strip()   '\'')

for_dict = eval('{'   e.replace('\n', ',')   '}')

print(for_dict)

# Get SportsAPING/v1.0/listMarketBook value
print(for_dict['betfairlightweight.exceptions.APIError'])

# Get ABC-0025 value
print(for_dict['Error']['message'])

# Get 2.0 value
print(for_dict['Full Response']['jsonrpc'])

# Get ['EX_TRADED'] value
print(for_dict['Params']['priceProjection']['priceData'])

Is there a more pythonic method or a module that handles this type of multiline string to be able to generate this dictionary in a cleaner and more professional way?

CodePudding user response:

this feels slightly subjective but here is how I would implement it (after spending all of a minute or two on it)

import ast
def line_to_dict_entry(line):
    key,value = line.strip().split(":",1)
    try:
        # try and parse it to a base pyobject
        value = ast.literal_eval(value.strip())
    except Exception as e:
        # just let it be a string
        value = value.strip()
    return key,value

data = dict(line_to_dict_entry(l) for l in e.splitlines(False))

print(data)

CodePudding user response:

import re
from ast import literal_eval

e = """betfairlightweight.exceptions.APIError: SportsAPING/v1.0/listMarketBook 
Params: {'marketIds': ['1.200293211'], 'priceProjection': {'priceData': ['EX_TRADED'], 'exBestOffersOverrides': {}, 'virtualise': True, 'rolloverStakes': False}} 
Exception: None 
Error: {'code': -32099, 'message': 'ANGX-0006', 'data': {'APINGException': {'requestUUID': '...........', 'errorCode': 'UNEXPECTED_ERROR', 'errorDetails': ''}, 'exceptionname': 'APINGException'}} 
Full Response: {'jsonrpc': '2.0', 'error': {'code': -32099, 'message': 'ANGX-0006', 'data': {'APINGException': {'requestUUID': '..........', 'errorCode': 'UNEXPECTED_ERROR', 'errorDetails': ''}, 'exceptionname': 'APINGException'}}, 'id': 1}"""


data = dict(x.split(':', 1) for x in e.splitlines())
data = {k.strip():literal_eval(v) if '{' in v else v.strip() for k,v in data.items()}
print(data)

Output:

{'betfairlightweight.exceptions.APIError': 'SportsAPING/v1.0/listMarketBook', 'Params': {'marketIds': ['1.200293211'], 'priceProjection': {'priceData': ['EX_TRADED'], 'exBestOffersOverrides': {}, 'virtualise': True, 'rolloverStakes': False}}, 'Exception': 'None', 'Error': {'code': -32099, 'message': 'ANGX-0006', 'data': {'APINGException': {'requestUUID': '...........', 'errorCode': 'UNEXPECTED_ERROR', 'errorDetails': ''}, 'exceptionname': 'APINGException'}}, 'Full Response': {'jsonrpc': '2.0', 'error': {'code': -32099, 'message': 'ANGX-0006', 'data': {'APINGException': {'requestUUID': '..........', 'errorCode': 'UNEXPECTED_ERROR', 'errorDetails': ''}, 'exceptionname': 'APINGException'}}, 'id': 1}}

Pretty Output:

import json

print(json.dumps(data, indent=3))

...

{
   "betfairlightweight.exceptions.APIError": "SportsAPING/v1.0/listMarketBook",
   "Params": {
      "marketIds": [
         "1.200293211"
      ],
      "priceProjection": {
         "priceData": [
            "EX_TRADED"
         ],
         "exBestOffersOverrides": {},
         "virtualise": true,
         "rolloverStakes": false
      }
   },
   "Exception": "None",
   "Error": {
      "code": -32099,
      "message": "ANGX-0006",
      "data": {
         "APINGException": {
            "requestUUID": "...........",
            "errorCode": "UNEXPECTED_ERROR",
            "errorDetails": ""
         },
         "exceptionname": "APINGException"
      }
   },
   "Full Response": {
      "jsonrpc": "2.0",
      "error": {
         "code": -32099,
         "message": "ANGX-0006",
         "data": {
            "APINGException": {
               "requestUUID": "..........",
               "errorCode": "UNEXPECTED_ERROR",
               "errorDetails": ""
            },
            "exceptionname": "APINGException"
         }
      },
      "id": 1
   }
}
  • Related