def escape_unmatched_angle_brackets(string, allowed_tag_fragments=()):
if not string:<EOL><INDENT>return string<EOL><DEDENT>tags = re.split('<STR_LIT>', string)<EOL>for i, val in enumerate(tags):<EOL><INDENT>if val.count('<STR_LIT:<>') == val.count('<STR_LIT:>>') and not val.startswith(allowed_tag_fragments):<EOL><INDENT>val = val.replace('<STR_LIT:<>', '<STR_LIT>')<EOL>val = val.replace('<STR_LIT:>>', '<STR_LIT>')<EOL><DEDENT>else:<EOL><INDENT>while val.count('<STR_LIT:<>') != val.count('<STR_LIT:>>'):<EOL><INDENT>if val.count('<STR_LIT:<>') != val.count('<STR_LIT:>>') and val.count('<STR_LIT:<>') > <NUM_LIT:0>:<EOL><INDENT>val = val.replace('<STR_LIT:<>', '<STR_LIT>', <NUM_LIT:1>)<EOL><DEDENT>elif val.count('<STR_LIT:<>') != val.count('<STR_LIT:>>') and val.count('<STR_LIT:>>') > <NUM_LIT:0>:<EOL><INDENT>val = val.replace('<STR_LIT:>>', '<STR_LIT>', <NUM_LIT:1>)<EOL><DEDENT><DEDENT>if val.count('<STR_LIT:<>') == val.count('<STR_LIT:>>') and not val.startswith(allowed_tag_fragments):<EOL><INDENT>val = escape_unmatched_angle_brackets(val, allowed_tag_fragments)<EOL><DEDENT><DEDENT>tags[i] = val<EOL><DEDENT>return '<STR_LIT>'.join(tags)<EOL>
In order to make an XML string less malformed, escape
unmatched less than tags that are not part of an allowed tag
Note: Very, very basic, and do not try regex \1 style replacements
  on unicode ever again! Instead this uses string replace
allowed_tag_fragments is a tuple of tag name matches for use with startswith()