Unless you have further restrictions on your input, your code cannot be correct. You're assuming there can be tag nesting, so you should be making a parser.
As far as I can tell, your grammar is:
text -> text tag_open text tag_close text
text -> char_sequence
tag_open -> '[' identifier ']'
tag_close -> '[/' identifier ']'
char_sequence is some (possibly empty) sequence of characters that don't contain
identifier is some sequence of (probably alphanumeric + underscore) characters.
Now you could use something like PLY to build a syntax tree. You'll have to define your terminals:
char_sequence = r"[^\*"
tag_open = r"\[[a-zA-Z0-9_]+\]"
tag_close = r"\[/[a-zA-Z0-9_]+\]"
Now you've got to build your abstract syntax tree. I would advise a class
Node that contains a list of children, the associated tag, and the text in it. Thus, something like
def __init__(self, tag_name, text):
self.tag_name = tag_name
self.text = text
self.children = 
def add_child(self, child):
[tag]Hello![tag]Hi![/tag][/tag] to parse to a
Node that has the
Hello!, and a single child.
Once you have that, you can go ahead and define the grammar rules. PLY lets you do that with
"""text : text open_tag text close_tag text"""
# You now have an object for each of text, text, open_tag, text, close_tag, text
# and can put them together here.
Once you've defined all parser rules, you get an abstract syntax tree, which you can then traverse finding all relevant nodes and calling replace on the text you want.
As you can see, this method is a lot longer and a good deal more complicated. The token examples I showed don't match the grammar -- you'd probably want to split the
] out of the tokens, and make sure that
open_tag just contain the identifiers. I've also not entirely specified how the grammar rules should be implemented: the current setup leads to it being unclear where the text is in relation to the tags. You'd have to refine the system somewhat to make that work (probably making the text a kind of child, and appending it at a suitable time.
Because of this, you should first try to restrict your input. If you can ensure a number of things about tag nesting (especially tags containing themselves) it would allow you to do all of this with a regex, and in that case Winston Ewert's solution is what you should go with.