Tools: Update binding generator.
This commit is contained in:
@@ -156,13 +156,13 @@ macro(generate_from_py BASE_NAME)
|
||||
if(NOT EXISTS "${SOURCE_CPP_PATH}")
|
||||
# assures the source files are generated at least once
|
||||
message(STATUS "${SOURCE_CPP_PATH}")
|
||||
execute_process(COMMAND "${PYTHON_EXECUTABLE}" "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" "${SOURCE_NATIVE_PATH}"
|
||||
execute_process(COMMAND "${Python3_EXECUTABLE}" "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" "${SOURCE_NATIVE_PATH}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
||||
endif()
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME}_.h" "${CMAKE_CURRENT_BINARY_DIR}/${BASE_NAME}_.cpp"
|
||||
COMMAND ${PYTHON_EXECUTABLE} "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" ${BASE_NAME}.pyi
|
||||
COMMAND ${Python3_EXECUTABLE} "${TOOL_NATIVE_PATH}" --outputPath "${OUTPUT_NATIVE_PATH}" ${BASE_NAME}.pyi
|
||||
MAIN_DEPENDENCY "${BASE_NAME}.pyi"
|
||||
DEPENDS
|
||||
"${CMAKE_SOURCE_DIR}/src/Tools/bindings/templates/templateClassPyExport.py"
|
||||
|
||||
@@ -57,7 +57,7 @@ def generate(filename, outputPath):
|
||||
Export.outputDir = outputPath + "/"
|
||||
Export.inputDir = os.path.dirname(filename) + "/"
|
||||
Export.export = GenerateModelInst.PythonExport[0]
|
||||
Export.is_python = filename.endswith(".py")
|
||||
Export.is_python = filename.endswith(".pyi")
|
||||
Export.Generate()
|
||||
if Export.is_python:
|
||||
Export.Compare()
|
||||
|
||||
@@ -210,10 +210,17 @@ def _parse_methods(class_node: ast.ClassDef) -> List[Methode]:
|
||||
"""
|
||||
methods = []
|
||||
|
||||
for stmt in class_node.body:
|
||||
if not isinstance(stmt, ast.FunctionDef):
|
||||
continue
|
||||
def collect_function_defs(nodes):
|
||||
funcs = []
|
||||
for node in nodes:
|
||||
if isinstance(node, ast.FunctionDef):
|
||||
funcs.append(node)
|
||||
elif isinstance(node, ast.If):
|
||||
funcs.extend(collect_function_defs(node.body))
|
||||
funcs.extend(collect_function_defs(node.orelse))
|
||||
return funcs
|
||||
|
||||
for stmt in collect_function_defs(class_node.body):
|
||||
# Skip methods decorated with @overload
|
||||
skip_method = False
|
||||
for deco in stmt.decorator_list:
|
||||
@@ -542,28 +549,42 @@ def _parse_class(class_node, source_code: str, path: str, imports_mapping: dict)
|
||||
def parse_python_code(path: str) -> GenerateModel:
|
||||
"""
|
||||
Parse the given Python source code and build a GenerateModel containing
|
||||
PythonExport entries for each class that inherits from a relevant binding class.
|
||||
PythonExport entries. If any class is explicitly exported using @export,
|
||||
only those classes are used. If no classes have the @export decorator,
|
||||
then a single non-exported class is assumed to be the export. If there
|
||||
are multiple non-exported classes, an exception is raised.
|
||||
"""
|
||||
|
||||
source_code = None
|
||||
with open(path, "r") as file:
|
||||
source_code = file.read()
|
||||
|
||||
tree = ast.parse(source_code)
|
||||
imports_mapping = _parse_imports(tree)
|
||||
model = GenerateModel()
|
||||
|
||||
explicit_exports = []
|
||||
non_explicit_exports = []
|
||||
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.ClassDef):
|
||||
py_export = _parse_class(node, source_code, path, imports_mapping)
|
||||
model.PythonExport.append(py_export)
|
||||
if py_export.IsExplicitlyExported:
|
||||
explicit_exports.append(py_export)
|
||||
else:
|
||||
non_explicit_exports.append(py_export)
|
||||
|
||||
# Check for multiple non explicitly exported classes
|
||||
non_exported_classes = [
|
||||
item for item in model.PythonExport if not getattr(item, "IsExplicitlyExported", False)
|
||||
]
|
||||
if len(non_exported_classes) > 1:
|
||||
raise Exception("Multiple non explicitly-exported classes were found, please use @export.")
|
||||
model = GenerateModel()
|
||||
if explicit_exports:
|
||||
# Use only explicitly exported classes.
|
||||
model.PythonExport.extend(explicit_exports)
|
||||
else:
|
||||
# No explicit exports; allow only one non-exported class.
|
||||
if len(non_explicit_exports) == 1:
|
||||
model.PythonExport.append(non_explicit_exports[0])
|
||||
elif len(non_explicit_exports) > 1:
|
||||
raise Exception(
|
||||
"Multiple non explicitly-exported classes were found, please use @export."
|
||||
)
|
||||
else:
|
||||
raise Exception("No classes found for export.")
|
||||
|
||||
return model
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ def compareFiles(file1, file2):
|
||||
# Check if files exist
|
||||
for file in (file1, file2):
|
||||
if not os.path.exists(file):
|
||||
raise FileNotFoundError(f"File not found: {file1} {file2}")
|
||||
raise FileNotFoundError(f"File not found: {file}")
|
||||
|
||||
# Read file contents
|
||||
with open(file1, "r", encoding="utf-8") as f1, open(file2, "r", encoding="utf-8") as f2:
|
||||
|
||||
Reference in New Issue
Block a user