package python import ( "github.com/pkg/errors" "github.com/pulumi/pulumi/pkg/v3/codegen" "io" "regexp" "strings" "unicode" ) // isLegalIdentifierStart returns true if it is legal for c to be the first character of a Python identifier as per // https://docs.python.org/3.7/reference/lexical_analysis.html#identifiers. func isLegalIdentifierStart(c rune) bool { return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_' || unicode.In(c, unicode.Lu, unicode.Ll, unicode.Lt, unicode.Lm, unicode.Lo, unicode.Nl) } // isLegalIdentifierPart returns true if it is legal for c to be part of a Python identifier (besides the first // character) as per https://docs.python.org/3.7/reference/lexical_analysis.html#identifiers. func isLegalIdentifierPart(c rune) bool { return isLegalIdentifierStart(c) || c >= '0' && c <= '9' || unicode.In(c, unicode.Lu, unicode.Ll, unicode.Lt, unicode.Lm, unicode.Lo, unicode.Nl, unicode.Mn, unicode.Mc, unicode.Nd, unicode.Pc) } // isLegalIdentifier returns true if s is a legal Python identifier as per // https://docs.python.org/3.7/reference/lexical_analysis.html#identifiers. func isLegalIdentifier(s string) bool { reader := strings.NewReader(s) c, _, _ := reader.ReadRune() if !isLegalIdentifierStart(c) { return false } for { c, _, err := reader.ReadRune() if err != nil { return err == io.EOF } if !isLegalIdentifierPart(c) { return false } } } // makeValidIdentifier replaces characters that are not allowed in Python identifiers with underscores. No attempt is // made to ensure that the result is unique. func makeValidIdentifier(name string) string { var builder strings.Builder for i, c := range name { if !isLegalIdentifierPart(c) { builder.WriteRune('_') } else { if i == 0 && !isLegalIdentifierStart(c) { builder.WriteRune('_') } builder.WriteRune(c) } } return builder.String() } func makeSafeEnumName(name, typeName string) (string, error) { // Replace common single character enum names. safeName := codegen.ExpandShortEnumName(name) // If the name is one illegal character, return an error. if len(safeName) == 1 && !isLegalIdentifierStart(rune(safeName[0])) { return "", errors.Errorf("enum name %s is not a valid identifier", safeName) } // If it's camelCase, change it to snake_case. safeName = PyName(safeName) // Change to uppercase and make a valid identifier. safeName = makeValidIdentifier(strings.ToTitle(safeName)) // If the enum name starts with an underscore, add the type name as a prefix. if strings.HasPrefix(safeName, "_") { pyTypeName := strings.ToTitle(PyName(typeName)) safeName = pyTypeName + safeName } // If there are multiple underscores in a row, replace with one. regex := regexp.MustCompile(`_+`) safeName = regex.ReplaceAllString(safeName, "_") return safeName, nil }