0

I'm trying to parse a string that has been formatted using inspect.formatargvalues and I want to be able to extract the argument values and save them in a dictionary.

Here are some examples of the text:

CALL function.a(timeout_secs=10)
CALL function.b(x=something good (happy days, ABC 123), y_z=None, check_this=True, check_this_2=None, extra_info=None, press=False, timeout_secs=30)
CALL function.c(key='A_B')
CALL function.d(target=<Tab.Chrome: 'tab-chrome'>)
CALL function.e(music_type=<MusicType.ALBUM: 1>, term=None, genre=None, results=True)
CALL function.f(button='END', delay=None)

At the moment I'm extracting the parameters using regex to get:

timeout_secs=10
x=something good (happy days, ABC 123), y_z=None, check_this=True, check_this_2=None, extra_info=None, press=False, timeout_secs=30
key='A_B'
target=<Tab.Chrome: 'tab-chrome'>
music_type=<MusicType.ALBUM: 1>, term=None, genre=None, results=True
button='END', delay=None

Then I'm splitting on ',' and then converting the array into a dictionary.

dict(parameter.split("=") for parameter in array_of_parameters)

This works for the most part but obviously breaks when parsing:

x=something good (happy days, ABC 123), y_z=None, check_this=True, check_this_2=None, extra_info=None, press=False, timeout_secs=30

Is there a way to convert the arguments to a dictionary where it covers all these scenarios?

QCFia
  • 39
  • 4
  • Could you use keyword arguments instead of named arguments in your functions? Keyword arguments would make all of your arguments accessible via a dictionary. [https://stackoverflow.com/questions/1419046/python-normal-arguments-vs-keyword-arguments](https://stackoverflow.com/questions/1419046/python-normal-arguments-vs-keyword-arguments) – vielkind May 10 '18 at 17:04

1 Answers1

0

It's impossible to find a solution that is guaranteed to work in all cases, since the argument values can contain anything and this isn't a proper data format designed to be parsed. Here is something that works a bit better:

import re

line = 'x=something good (happy days, ABC 123), y_z=None, check_this=True, check_this_2=None, extra_info=None, press=False, timeout_secs=30'

print(re.findall(r'(?:^|, )(\w+)=(.+?)(?=$|, \w+=)', line))
# [('x', 'something good (happy days, ABC 123)'), ('y_z', 'None'), ('check_this', 'True'), ('check_this_2', 'None'), ('extra_info', 'None'), ('press', 'False'), ('timeout_secs', '30')]

Explanation:

  1. (?:^|, ): the argument must be at the start of the string or preceded by a comma and space. This is a non-capturing group to contain the effect of | while not appearing in the results of findall.
  2. (\w+): the argument name
  3. (.+?): the argument value.
  4. (?=$|, \w+=): the argument value must be followed by the end of the string or another argument. This is a lookahead assertion instead of just a non-capturing group so that the match doesn't consume the string. Otherwise findall only finds every second argument since it only finds non-overlapping matches.
Alex Hall
  • 34,833
  • 5
  • 57
  • 89