Sanitize and represent a string argument in Gremlin.

def _safe_gremlin_string(value):
    """Sanitize and represent a string argument in Gremlin."""
    if not isinstance(value, six.string_types):
        if isinstance(value, bytes):  # should only happen in py3
            value = value.decode('utf-8')
        else:
            raise GraphQLInvalidArgumentError(u'Attempting to convert a non-string into a string: '
                                              u'{}'.format(value))

    # Using JSON encoding means that all unicode literals and special chars
    # (e.g. newlines and backslashes) are replaced by appropriate escape sequences.
    # However, the quoted result is wrapped in double quotes, and $ signs are not escaped,
    # so that would allow arbitrary code execution in Gremlin.
    # We will therefore turn the double-quoted string into a single-quoted one to avoid this risk.
    escaped_and_quoted = json.dumps(value)

    # Double-quoted string literals in Gremlin/Groovy allow
    # arbitrary code execution via string interpolation and closures.
    # To avoid this, we perform the following steps:
    #   - we strip the wrapping double quotes;
    #   - we un-escape any double-quotes in the string, by replacing \" with ";
    #   - we escape any single-quotes in the string, by replacing ' with \';
    #   - finally, we wrap the string in single quotes.
    # http://www.groovy-lang.org/syntax.html#_double_quoted_string
    if not escaped_and_quoted[0] == escaped_and_quoted[-1] == '"':
        raise AssertionError(u'Unreachable state reached: {} {}'.format(value, escaped_and_quoted))
    no_quotes = escaped_and_quoted[1:-1]
    re_escaped = no_quotes.replace('\\"', '"').replace('\'', '\\\'')

    final_escaped_value = '\'' + re_escaped + '\''
    return final_escaped_value