ROBERT RICHARDSON
ROBERT RICHARDSON

Reputation: 2279

Python: Identical string slicing, two different results

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.

Upvotes: 0

Views: 101

Answers (2)

jez
jez

Reputation: 15349

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.

Upvotes: 0

A. Sokol
A. Sokol

Reputation: 336

It looks like this line:

dbErrorString = errorString[openParenLocation + 1:closeParenLocation]

should instead be:

dbErrorString = strippedString[openParenLocation + 1:closeParenLocation]

Upvotes: 1

Related Questions