Process a string to produce a valid python variable/class/type name

    Arguments:
      space (str): string to substitute for spaces ('' to delete all whitespace)
      camel (bool): whether to camel-case names, Django Model Name style (first letter capitalized)
      lower (bool): whether to lowercase all strings
      language (str): case-insensitive language identifier (to deterimine allowable identifier characters)
        e.g. 'Python', 'Python2', 'Python3', 'Javascript', 'ECMA'

    Examples:
      Generate Django model names out of file names
      >>> make_name('women in IT.csv', camel=True)
      'WomenInItCsv'

      Generate Django field names out of CSV header strings
      >>> make_name('ID Number (9-digits)')
      'id_number_9_digits_'
      >>> make_name("PD / SZ")
      'pd_sz'

      Generate Javscript object attribute names from CSV header strings
      >>> make_name('pi (\u03C0)', space = '', language='javascript')
      'pi\u03c0'
      >>> make_name('pi (\u03C0)', space = '', language='javascript')
      'pi\u03c0'

def make_name(s, camel=None, lower=None, space='_', remove_prefix=None, language='python', string_type=str):
    """Process a string to produce a valid python variable/class/type name

    Arguments:
      space (str): string to substitute for spaces ('' to delete all whitespace)
      camel (bool): whether to camel-case names, Django Model Name style (first letter capitalized)
      lower (bool): whether to lowercase all strings
      language (str): case-insensitive language identifier (to deterimine allowable identifier characters)
        e.g. 'Python', 'Python2', 'Python3', 'Javascript', 'ECMA'

    Examples:
      Generate Django model names out of file names
      >>> make_name('women in IT.csv', camel=True)
      'WomenInItCsv'

      Generate Django field names out of CSV header strings
      >>> make_name('ID Number (9-digits)')
      'id_number_9_digits_'
      >>> make_name("PD / SZ")
      'pd_sz'

      Generate Javscript object attribute names from CSV header strings
      >>> make_name('pi (\u03C0)', space = '', language='javascript')
      'pi\u03c0'
      >>> make_name('pi (\u03C0)', space = '', language='javascript')
      'pi\u03c0'
    """
    if camel is None and lower is None:
        lower = True
    if not s:
        return None
    ecma_languages = ['ecma', 'javasc']
    unicode_languages = ecma_languages
    language = language or 'python'
    language = language.lower().strip()[:6]
    string_type = string_type or str
    if language in unicode_languages:
        string_type = str
    s = string_type(s)  # TODO: encode in ASCII, UTF-8, or the charset used for this file!
    if remove_prefix and s.startswith(remove_prefix):
        s = s[len(remove_prefix):]
    if camel:
        if space and space == '_':
            space = ''
        if any(c in ' \t\n\r' + string.punctuation for c in s) or s.lower() == s:
            if lower:
                s = s.lower()
            s = s.title()
    elif lower:
        s = s.lower()
    # TODO: add language Regexes to filter characters appropriately for python or javascript
    space_escape = '\\' if space and space not in ' _' else ''
    if language not in ecma_languages:
        invalid_char_regex = re.compile('[^a-zA-Z0-9' + space_escape + space + ']+')
    else:
        # FIXME: Unicode categories and properties only works in Perl Regexes!
        invalid_char_regex = re.compile('[\W' + space_escape + space + ']+', re.UNICODE)
    if space is not None:
        # get rid of all invalid characters, substitting the space-filler for them all
        s = invalid_char_regex.sub(space, s)
        # get rid of duplicate space-filler characters
        if space:
            s = re.sub('[' + space_escape + space + ']{2,}', space, s)
    return s