Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically update when a reserved keyword is used as an enum in ACT (Python bindings) #207

Merged
merged 2 commits into from
Sep 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ Examples/**/obj
debug
.vscode

.DS_Store
.DS_Store

# Jetbrains
.idea/
41 changes: 41 additions & 0 deletions Source/actutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,52 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package main

import (
"errors"
"fmt"
"log"
"os"
)

var (
ErrPythonBuildFailed = errors.New("failed to build dynamic Python implementation")
ErrFileDeletionFailed = errors.New("failed to write to output file")
ErrReservedKeyword = errors.New("failed to generate bindings as you are using a reserved keyword")
)

// Keep a map of reserved keywords in Python
var pythonReservedKeywords = map[string]bool{
"False": true, "None": true, "True": true, "and": true, "as": true, "assert": true, "async": true,
"await": true, "break": true, "class": true, "continue": true, "def": true, "del": true, "elif": true,
"else": true, "except": true, "finally": true, "for": true, "from": true, "global": true, "if": true,
"import": true, "in": true, "is": true, "lambda": true, "nonlocal": true, "not": true, "or": true,
"pass": true, "raise": true, "return": true, "try": true, "while": true, "with": true, "yield": true,
}

// FileExists returns true if and only if the file in a given path exists
func FileExists(path string) (bool) {
_, err := os.Stat(path);
return !os.IsNotExist(err);
}

// ReservedKeywordExit logs the formatted error, deletes the partially created file, and returns a named error.
func ReservedKeywordExit(bindingPath string, format string, a ...interface{}) error {
// Format the message using variadic arguments
msg := fmt.Sprintf(format, a...)

// Log the error message
log.Printf("%s", msg)

// Attempt to delete the partially created file
log.Printf("Deleting partially created binding file: %s", bindingPath)
err := os.Remove(bindingPath)
if err != nil {
// Log and return a wrapped error with context
log.Printf("Failed to delete incomplete file %s: %v", bindingPath, err)
return fmt.Errorf("%w: %v", ErrFileDeletionFailed, err)
}

log.Printf("Deleted binding file")

// Return the reserved keyword error
return ErrReservedKeyword
}
9 changes: 6 additions & 3 deletions Source/automaticcomponenttoolkit.go
Original file line number Diff line number Diff line change
Expand Up @@ -732,10 +732,13 @@ func main() {

err = createComponent(component, outfolderBase, bindingsDirectoryOverride, interfacesDirectoryOverride, stubDirectoryOverride, suppressBindings, suppressStub, suppressInterfaces, suppressSubcomponents, suppressLicense, suppressExamples)
if (err != nil) {
log.Println("Fatal error")
log.Fatal(err)
if err == ErrPythonBuildFailed {
log.Println("Python binding generation failed (Due to usage of reserved keywords)")
} else {
log.Println("Fatal error")
log.Fatal(err)
}
} else {
log.Println("Success")
}

}
45 changes: 41 additions & 4 deletions Source/buildbindingpython.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ import (
"strings"
)

// Store the python file path
var pythonBindingFile = "";


// BuildBindingPythonDynamic builds dynamic Python bindings of a library's API in form of explicitly loaded
// functions handles.
func BuildBindingPythonDynamic(componentdefinition ComponentDefinition, outputFolder string, outputFolderExample string, indentString string) error {
Expand All @@ -50,6 +54,7 @@ func BuildBindingPythonDynamic(componentdefinition ComponentDefinition, outputFo
libraryname := componentdefinition.LibraryName

DynamicPythonImpl := path.Join(outputFolder, namespace+".py");
pythonBindingFile = DynamicPythonImpl;
log.Printf("Creating \"%s\"", DynamicPythonImpl)
dynpythonfile, err := CreateLanguageFile (DynamicPythonImpl, indentString)
if err != nil {
Expand All @@ -62,6 +67,9 @@ func BuildBindingPythonDynamic(componentdefinition ComponentDefinition, outputFo

err = buildDynamicPythonImplementation(componentdefinition, dynpythonfile)
if err != nil {
if err == ErrReservedKeyword {
return ErrPythonBuildFailed
}
return err;
}

Expand Down Expand Up @@ -141,6 +149,9 @@ func buildDynamicPythonImplementation(componentdefinition ComponentDefinition, w
w.Writeln(" SUCCESS = 0")
for i := 0; i<len(componentdefinition.Errors.Errors); i++ {
merror := componentdefinition.Errors.Errors[i]
if pythonReservedKeywords[merror.Name] {
return ReservedKeywordExit(pythonBindingFile, "Error code uses a reserved keyword : %s", merror.Name)
}
w.Writeln(" %s = %d", merror.Name, merror.Code)
}
w.Writeln("")
Expand Down Expand Up @@ -174,12 +185,20 @@ func buildDynamicPythonImplementation(componentdefinition ComponentDefinition, w

for i := 0; i<len(componentdefinition.Enums); i++ {
enum := componentdefinition.Enums[i]
if pythonReservedKeywords[enum.Name] {
return ReservedKeywordExit(pythonBindingFile, "Class name for enum uses a reserved keyword : %s", enum.Name)
}
w.Writeln("'''Definition of %s", enum.Name)
w.Writeln("'''")
w.Writeln("class %s(CTypesEnum):", enum.Name)
for j:= 0; j<len(enum.Options); j++ {
option := enum.Options[j]
w.Writeln(" %s = %d", option.Name, option.Value)
if pythonReservedKeywords[option.Name] {
log.Printf("Invalid enum datatype as \"%s\" is a reserved keyword in Python. Replacing it with \"%s\"", option.Name, enum.Name + option.Name)
w.Writeln(" %s = %d", enum.Name + option.Name, option.Value)
} else {
w.Writeln(" %s = %d", option.Name, option.Value)
}
}
}
w.Writeln("")
Expand All @@ -190,6 +209,9 @@ func buildDynamicPythonImplementation(componentdefinition ComponentDefinition, w
w.Writeln("'''")
for i := 0; i<len(componentdefinition.Structs); i++ {
_struct := componentdefinition.Structs[i]
if pythonReservedKeywords[_struct.Name] {
return ReservedKeywordExit(pythonBindingFile, "Class name for the structure uses a reserved keyword : %s", _struct.Name)
}
w.Writeln("'''Definition of %s", _struct.Name)
w.Writeln("'''")
w.Writeln("class %s(ctypes.Structure):", _struct.Name)
Expand Down Expand Up @@ -231,6 +253,9 @@ func buildDynamicPythonImplementation(componentdefinition ComponentDefinition, w
w.Writeln("'''")
for i := 0; i<len(componentdefinition.Functions); i++ {
_func := componentdefinition.Functions[i]
if pythonReservedKeywords[_func.FunctionName] {
return ReservedKeywordExit(pythonBindingFile, "Function type definition uses a reserved keyword : %s", _func.FunctionName)
}
w.Writeln("'''Definition of %s", _func.FunctionName)
w.Writeln(" %s", _func.FunctionDescription)
w.Writeln("'''")
Expand Down Expand Up @@ -450,7 +475,9 @@ func writeFunctionTableMethod(method ComponentDefinitionMethod, w LanguageWriter
if err != nil {
return err
}

if pythonReservedKeywords[linearMethodName] {
return ReservedKeywordExit(pythonBindingFile, "Method name uses a reserved keyword : %s", linearMethodName)
}
w.Writeln("err = symbolLookupMethod(ctypes.c_char_p(str.encode(\"%s\")), methodAddress)", linearMethodName)
w.Writeln("if err != 0:")
w.Writeln(" raise E%sException(ErrorCodes.COULDNOTLOADLIBRARY, str(err))", NameSpace)
Expand Down Expand Up @@ -750,7 +777,9 @@ func generateCTypesParameter(param ComponentDefinitionParam, className string, m

func writePythonClass(component ComponentDefinition, class ComponentDefinitionClass, w LanguageWriter, NameSpace string) error {
pythonBaseClassName := fmt.Sprintf("%s", component.Global.BaseClassName)

if pythonReservedKeywords[pythonBaseClassName] {
return ReservedKeywordExit(pythonBindingFile, "Class implementation name uses a reserved keyword : %s", pythonBaseClassName)
}
w.Writeln("''' Class Implementation for %s", class.ClassName)
w.Writeln("'''")

Expand All @@ -764,8 +793,14 @@ func writePythonClass(component ComponentDefinition, class ComponentDefinitionCl
w.Writeln("class %s(%s):", class.ClassName, parentClass)
w.Writeln(" def __init__(self, handle, wrapper):")
w.Writeln(" %s.__init__(self, handle, wrapper)", parentClass)
if pythonReservedKeywords[class.ClassName] || pythonReservedKeywords[parentClass] {
return ReservedKeywordExit(pythonBindingFile, "Class implementation name uses a reserved keyword : %s, %s", class.ClassName, parentClass)
}

} else {
if pythonReservedKeywords[class.ClassName] {
return ReservedKeywordExit(pythonBindingFile, "Class implementation name uses a reserved keyword : %s", class.ClassName)
}
w.Writeln("class %s:", class.ClassName)

w.Writeln(" def __init__(self, handle, wrapper):")
Expand Down Expand Up @@ -1002,7 +1037,9 @@ func writeMethod(method ComponentDefinitionMethod, w LanguageWriter, NameSpace s
}

exportName := GetCExportName(NameSpace, ClassName, method, isGlobal)

if pythonReservedKeywords[method.MethodName] {
return ReservedKeywordExit(pythonBindingFile, "Method name uses a reserved keyword : %s", method.MethodName)
}
w.Writeln(" def %s(self%s):", method.MethodName, pythonInParams)
w.Writelns(" ", preCallLines)
if (doCheckCall) {
Expand Down
Loading