For that kind of parsing itertools.groupby is the right tool.
With groupby, as I used it down below, every group is either: <,> or an actual piece of code (like foo). So the only thing left to do is to increment/decrement the depth level and decide if the group should be appended or not to the final result list.
from itertools import groupby
def remove_arguments(template, depth):
res = []
curr_depth = 0
for k,g in groupby(template, lambda x: x in ['<', '>']):
text = ''.join(g) # rebuild the group as a string
if text == '<':
curr_depth += 1
# it's important to put this part in the middle
if curr_depth < depth:
res.append(text)
elif k and curr_depth == depth: # add the inner <>
res.append(text)
if text == '>':
curr_depth -= 1
return ''.join(res) # rebuild the complete string
It's important to put the depth-level check in between the increment and decrement part, because it has to be decided if the </> are in or out the current depth level.
Output examples:
>>> remove_arguments('foo<int>()', 1)
foo<>()
>>> remove_arguments('foo< bar<int> >()', 1)
foo<>()
>>> remove_arguments('foo< bar<int> >()', 2)
foo< bar<> >()
>>> remove_arguments('foo< bar >()', 2)
foo< bar >()
Also, a couple of quick style notes:
- Don't use
str as variable name or you'll shadow the builitin str.
- Don't use
CamelCase for functions/variable names (look at PEP8).
Inspired by lvc comment, I've grouped here possible lambdas/functions to be used in groupby:
groupby(template, lambda x: x in ['<', '>']) # most obvious one
groupby(template, lambda x: x in '<>') # equivalent to the one above
groupby(template, '<>'.__contains__) # this is ugly ugly
groupby(template, '<>'.count) # not obvious, but looks sweet
Update
To handle cases like: foo<bar<int>>(), you'll need a better groupby key function. To be specific, a key function that return the current depth-level for a given charachter.
Like this one:
def get_level(ch, level=[0]):
current = level[0]
if ch == '<':
level[0] += 1
if ch == '>':
level[0] -= 1
current = level[0]
return current
That take advantage of the mutable argument level to perform some kind of memoization.
Observe that remove_arguments will now be more simple:
def remove_arguments(template, depth):
res = []
for k,g in groupby(template, get_level):
if k < depth:
res.append(''.join(g))
return ''.join(res)