0

Somewhere I'm being an idiot, but I can't find where.

I'm running a Python script using a PostgreSQL database through ODBC. I am trying to extract the meaningful piece from a database exception message. Here is the raw message, with line breaks added for readability:

(-2147352567, 'Exception occurred.', (0, 'Microsoft OLE DB Provider for ODBC Drivers', 
'ERROR: Charge not in a correct status to delete;\nError while executing the query', 
None, 0, -2147467259), None)

Note that there are two sets of parentheses in this string. First, I find the locations of the outer ones and slice them off. This gives the expected result:

-2147352567, 'Exception occurred.', (0, 'Microsoft OLE DB Provider for ODBC Drivers', 
'ERROR: Charge not in a correct status to delete;\nError while executing the query', 
None, 0, -2147467259), None

Then, using as far as I can tell identical code, I strip off the other set of parentheses and everything outside them. This gives this result:

(0, 'Microsoft OLE DB Provider for ODBC Drivers', 
'ERROR: Charge not in a correct status to delete;\nError while executing the query', 
None, 0, -214746725

The open parenthesis is still here, even though I am using the result of the find() method the same way, adding one to the open parenthesis location as the start of the slice, both times.

Here is the code:

    print (errorString)
    openParenLocation = errorString.find('(')
    closeParenLocation = errorString.rfind(')')
    strippedString = errorString[openParenLocation + 1:closeParenLocation]
    openParenLocation = strippedString.find('(')
    closeParenLocation = strippedString.rfind(')')
    dbErrorString = errorString[openParenLocation + 1:closeParenLocation]
    print (strippedString)
    print ("{}, {}".format(openParenLocation, closeParenLocation))
    print (dbErrorString)

And here is the raw output, with no added line breaks:

(-2147352567, 'Exception occurred.', (0, 'Microsoft OLE DB Provider for ODBC Drivers', 'ERROR: Charge not in a correct status to delete;\nError while executing the query', None, 0, -2147467259), None)
-2147352567, 'Exception occurred.', (0, 'Microsoft OLE DB Provider for ODBC Drivers', 'ERROR: Charge not in a correct status to delete;\nError while executing the query', None, 0, -2147467259), None
36, 191
(0, 'Microsoft OLE DB Provider for ODBC Drivers', 'ERROR: Charge not in a correct status to delete;\nError while executing the query', None, 0, -214746725

Test code using a much smaller string works as expected:

    testString = "(abc(def)ghij)"
    openParenLocation = testString.find('(')
    closeParenLocation = testString.rfind(')')
    strippedTestString = testString[openParenLocation + 1:closeParenLocation]
    openParenLocation = strippedTestString.find('(')
    closeParenLocation = strippedTestString.rfind(')')
    finalTestString = strippedTestString[openParenLocation + 1:closeParenLocation]

Thank you very much.

5
  • errorString[openParenLocation + 1:closeParenLocation] ??? what are you trying to do by using openParenLocation + 1:closeParenLocation ? Commented Jan 9, 2017 at 18:08
  • How are you getting/forming errorString originally? It looks a lot like a python tuple's repr. Perhaps the data is available as a tuple, which would make handling it a no brainer. To your question, you're forming the indices for dbErrorString fron strippedString, but then use errorString in the actual slicing instead of strippedString. Commented Jan 9, 2017 at 18:11
  • I am trying to strip off the outer open parenthesis and close parenthesis and everything outside them. The original string is the error string returned from ODBC because an error condition was encountered inside a database function. The string is not from Python and is not in a tuple. The goal of this effort is to return only the error message contained inside this long gobbledygook: "ERROR: Charge not in a correct status to delete". Commented Jan 9, 2017 at 18:14
  • Which library are you using to talk to your db? Commented Jan 9, 2017 at 18:19
  • And that string is most likely from python, as very few other languages/systems have a None, certainly not SQL. The contents of that string at least are a valid tuple... This reeks of an XY. Commented Jan 9, 2017 at 18:50

2 Answers 2

1

It looks like this line:

dbErrorString = errorString[openParenLocation + 1:closeParenLocation]

should instead be:

dbErrorString = strippedString[openParenLocation + 1:closeParenLocation]

Sign up to request clarification or add additional context in comments.

2 Comments

If you do want to use the original errorString again then you need to account for the change in index from the first strip, but since you overwrite the first openParenLocation, you would need to do some refactoring for this to be possible.
See? I knew it was something idiotic I was doing. Thanks very much!
0

Given that your string looks like Python syntax, have you considered using the standard ast library module to do all this work for you?

>>> errorString =r"""\
(-2147352567, 'Exception occurred.', (0, 'Microsoft OLE DB Provider for ODBC Drivers', 
'ERROR: Charge not in a correct status to delete;\nError while executing the query', 
None, 0, -2147467259), None)"""

>>> import ast
>>> a = ast.parse(errorString).body[0].value
>>> a
<_ast.Tuple at 0x10802d3d0>

>>> a.elts[0]
<_ast.Num at 0x10802d410>

>>> a.elts[0].n
-2147352567

>>> a.elts[1]
<_ast.Str at 0x10802d450>

>>> a.elts[1].s
'Exception occurred.'

>>> a.elts[2]
<_ast.Tuple at 0x10802d490>

>>> # so now lather/rinse repeat: iterate over a.elts[2].elts

>>> a.elts[3]
<_ast.Name at 0x10802d650>

>>> a.elts[3].id
'None'

An even simpler way would be to use ast.literal_eval to turn the string directly into the Python object that it describes. It's like the builtin eval, but safe from the security perspective because it will not evaluate anything that isn't a literal (so, any malicious errorString content will not be executed).

>>> a = ast.literal_eval(errorString)
>>> a[0]
-2147352567
>>> a[1]
'Exception occurred.'
>>> a[2][0]
0

etc.

4 Comments

I don't care for this solution. We are not Python experts here, and it is important that everyone who might look at my code be able to understand it. I have no idea what an Abstract Syntax Tree is, and I don't think it's a good idea to ask me or my coworkers to learn that concept in order to understand my code.
eval(errorString) would return the nested tuple object that errorString appears to describe. It could then be manipulated like any other Python object. It's safe to try it out on your example and play with it, but see the caveat in the edited answer.
Further update: I've just learned about ast.literal_eval which I will be using heavily in future. Would save you a lot of work here.
I begin to see and understand! Yes, it is much easier than what I wrote, and easy enough to explain or comment

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.