Functions to convert camelCase strings to snake_case I have written two functions, one actual and one helper, to convert camelCase strings into snake_case strings (I call it joint-lower case). Any ideas for improvement are welcome.

def _cc2jl(string):
    """Camel case to joint-lower helper."""

    for index, current in enumerate(string):

        if is_upper(current):
            if index &gt; 0:
                previous = string[index-1]

                if is_upper(previous):
                    try:
                        next = string[index+1]
                    except IndexError:
                        yield current.lower()
                        continue
                    else:
                        if is_upper(next):
                            yield current.lower()
                            continue

                yield '_' + current.lower()
                continue

            yield current.lower()
            continue

        yield current


def cc2jl(string):
    """Converts a camel case string to joint-lower."""

    return ''.join(_cc2jl(string))


Expected behaviour:

&gt;&gt;&gt; cc2jl('m')
'm'
&gt;&gt;&gt; cc2jl('AA')
'aa'
&gt;&gt;&gt; cc2jl('MySQLDatabase')
'my_sql_database'
&gt;&gt;&gt; cc2jl('GarbageCollection')
'garbage_collection'
&gt;&gt;&gt; cc2jl('AAAAAAAAAAAAAAAAA')
'aaaaaaaaaaaaaaaaa'
A couple of things:


Why 2 functions?
Now that we established (1), you can get rid of the yields. 
yield is useful when you want to process stuff in chunks and do not want to wait for the entire population to be created first. This is not the case here.
There are some continues that do make sense and some that are redundant. Yours are of the latter type.
Nested if statements without corresponding else clauses can be merged with and if readability is not reduced.


Putting all that together, we get the following:

def cc2jl(my_str):
  """Camel case to joint-lower"""

  r = my_str[0].lower()
  for i, letter in enumerate(my_str[1:], 1):
    if letter.isupper():
      try:
        if my_str[i-1].islower() or my_str[i+1].islower():
          r += '_'
      except IndexError:
        pass
    r += letter.lower()
  return r

print(cc2jl('m'))                  #-&gt; m
print(cc2jl('AA'))                 #-&gt; aa
print(cc2jl('MySQLDatabase'))      #-&gt; my_sql_database
print(cc2jl('GarbageCollection'))  #-&gt; garbage_collection
print(cc2jl('AAAAAAAAAAAAAAAAA'))  #-&gt; aaaaaaaaaaaaaaaaa




Finally, some thoughts on avoiding the try block that bothers me personally.

We can leverage from the short-circuiting used in Python's logical expressions and (re-)write the following:

def cc2jl(my_str):
  """Camel case to joint-lower"""

  r = my_str[0].lower()
  for i, letter in enumerate(my_str[1:], 1):
    if letter.isupper():
      if my_str[i-1].islower() or (i != len(my_str)-1 and my_str[i+1].islower()):
        r += '_'
    r += letter.lower()
  return r


Note the i != len(my_str)-1 on the left side of the and. If this returns False the my_str[i+1] is not evaluated at all and thus cannot raise the IndexError.