remove_prefix and remove_suffix functions
Firstly I would say turn your print statements into a test. That way you can change the implementation with confidence that you have not broken anything.

Here is what those debug prints are as a test:

class TestRemove(unittest.TestCase):

    def test_remove_prefix(self):
        hello = 'Hello, World'
        value = remove_prefix(hello, 'Hello, ')
        self.assertEqual(value, 'World')
        value = remove_prefix(hello, 'Hello, Hello', raise_if_no_match=False)
        self.assertEqual(value, hello)
        self.assertRaises(ValueError, remove_prefix, hello, 'Hello, Hello')

    def test_remove_suffix(self):
        singing = 'I am singing in the rain'
        value = remove_suffix(singing, ' in the rain')
        self.assertEqual(value, 'I am singing')
        value = remove_suffix(singing, 'swinging in the rain', raise_if_no_match=False)
        self.assertEqual(value, singing)
        self.assertRaises(ValueError, remove_suffix, singing, 'swinging in the rain')


I think you should be using the the built in string operations which can be found here. Namely startswith(), endswith(), spilt() and rsplit().

You can also reassign variable names to give a more clear flow through the function. Here is my version of remove_prefix().

def remove_prefix(text, prefix, raise_if_no_match=True):
    if (text.startswith(prefix)):
        text = text.split(prefix, 1)[1]
    else:
        if (raise_if_no_match):
            msg_fmt = 'Text "{}" does not end with a prefix "{}".'
            raise ValueError(msg_fmt.format(text, prefix))
    return text


From that I'm sure you can change remove_suffix().

Also I would add docstrings to the functions so the arguments and exceptions raised are documented.