Spaces:
Sleeping
Sleeping
Sync from GitHub
Browse files- .gitignore +1 -0
- src/__main__.py +18 -5
- src/compiler.py +9 -8
- src/interface.py +9 -11
- src/optimizer.py +18 -7
- src/prompts.py +21 -4
- src/tester.py +8 -4
.gitignore
CHANGED
|
@@ -9,6 +9,7 @@ __pycache__/
|
|
| 9 |
# Distribution / packaging
|
| 10 |
.Python
|
| 11 |
build/
|
|
|
|
| 12 |
develop-eggs/
|
| 13 |
dist/
|
| 14 |
downloads/
|
|
|
|
| 9 |
# Distribution / packaging
|
| 10 |
.Python
|
| 11 |
build/
|
| 12 |
+
compiled/
|
| 13 |
develop-eggs/
|
| 14 |
dist/
|
| 15 |
downloads/
|
src/__main__.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
"""Main entrypoint module for the AI Python C Extensions Generator application."""
|
| 2 |
|
| 3 |
from logging import getLogger
|
| 4 |
-
from os import getenv
|
|
|
|
| 5 |
from sys import platform
|
| 6 |
|
| 7 |
from .compiler import compile_extension
|
|
@@ -26,7 +27,19 @@ else:
|
|
| 26 |
default_platform = "Linux"
|
| 27 |
|
| 28 |
# Read compile stage flag from environment variable, defaulting to False if not set.
|
| 29 |
-
compile_stage = getenv("COMPILE_STAGE", "False").lower() in ("true", "1", "yes")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
|
| 32 |
def main():
|
|
@@ -35,13 +48,13 @@ def main():
|
|
| 35 |
_logger.info(f'AVAILABLE MODELS: {models}')
|
| 36 |
# Prepare the interface configuration based.
|
| 37 |
interface_config = {"models": models, "compile_stage": compile_stage,
|
| 38 |
-
"optimize_function": optimize_function
|
|
|
|
| 39 |
if compile_stage:
|
| 40 |
-
_logger.info('COMPILE STAGE ENABLED')
|
| 41 |
interface_config.update({"compile_extension": compile_extension,
|
| 42 |
"test_extension": test_extension})
|
| 43 |
app = get_interface(**interface_config)
|
| 44 |
-
app.launch(footer_links=[])
|
| 45 |
# We return the app instance for potential use in autoreload scenarios.
|
| 46 |
return app
|
| 47 |
|
|
|
|
| 1 |
"""Main entrypoint module for the AI Python C Extensions Generator application."""
|
| 2 |
|
| 3 |
from logging import getLogger
|
| 4 |
+
from os import getenv, sep
|
| 5 |
+
from pathlib import Path
|
| 6 |
from sys import platform
|
| 7 |
|
| 8 |
from .compiler import compile_extension
|
|
|
|
| 27 |
default_platform = "Linux"
|
| 28 |
|
| 29 |
# Read compile stage flag from environment variable, defaulting to False if not set.
|
| 30 |
+
if compile_stage := getenv("COMPILE_STAGE", "False").lower() in ("true", "1", "yes"):
|
| 31 |
+
# Define the build directory for compiled extensions.
|
| 32 |
+
BUILD_DIR = Path("compiled")
|
| 33 |
+
# BUILD_DIR.mkdir(parents=True, exist_ok=True)
|
| 34 |
+
_logger.info('COMPILE STAGE ENABLED')
|
| 35 |
+
_logger.info(f'COMPILE DIRECTORY SET TO: .{sep}{BUILD_DIR}')
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
# CSS styles for the interface, defining background colors for C and Python code areas.
|
| 39 |
+
css = """
|
| 40 |
+
.c_ext {background-color: #050;}
|
| 41 |
+
.python {background-color: #306998;}
|
| 42 |
+
"""
|
| 43 |
|
| 44 |
|
| 45 |
def main():
|
|
|
|
| 48 |
_logger.info(f'AVAILABLE MODELS: {models}')
|
| 49 |
# Prepare the interface configuration based.
|
| 50 |
interface_config = {"models": models, "compile_stage": compile_stage,
|
| 51 |
+
"optimize_function": optimize_function,
|
| 52 |
+
"compile_path": BUILD_DIR}
|
| 53 |
if compile_stage:
|
|
|
|
| 54 |
interface_config.update({"compile_extension": compile_extension,
|
| 55 |
"test_extension": test_extension})
|
| 56 |
app = get_interface(**interface_config)
|
| 57 |
+
app.launch(footer_links=[], css=css)
|
| 58 |
# We return the app instance for potential use in autoreload scenarios.
|
| 59 |
return app
|
| 60 |
|
src/compiler.py
CHANGED
|
@@ -10,14 +10,15 @@ _logger = getLogger(__name__)
|
|
| 10 |
|
| 11 |
|
| 12 |
# Define a function to write outputs to a file with a given filename.
|
| 13 |
-
def write_file(data,
|
| 14 |
"""Write data to a file with the specified filename."""
|
| 15 |
-
|
|
|
|
| 16 |
file.write(data)
|
| 17 |
|
| 18 |
|
| 19 |
# Extension compilation function.
|
| 20 |
-
def build_extension():
|
| 21 |
"""Compile the C extension using 'setup.py' and return the compilation output."""
|
| 22 |
# Set default COMSPEC to cmd.exe on Windows to avoid issues with some C compilers.
|
| 23 |
if sys.platform == "win32":
|
|
@@ -27,7 +28,7 @@ def build_extension():
|
|
| 27 |
try:
|
| 28 |
_logger.info('STARTING COMPILATION PROCESS...')
|
| 29 |
compile_cmd = ["python", "setup.py", "build_ext", "--inplace"]
|
| 30 |
-
compile_result = subprocess.run(compile_cmd, env=os.environ,
|
| 31 |
check=True, text=True, capture_output=True)
|
| 32 |
except subprocess.CalledProcessError as ex:
|
| 33 |
_logger.error(f"COMPILATION FAILED WITH ERROR:\n{ex.stdout}\n{ex.stderr}")
|
|
@@ -42,19 +43,19 @@ def build_extension():
|
|
| 42 |
|
| 43 |
|
| 44 |
# Extension compilation function.
|
| 45 |
-
def compile_extension(c_code, setup_code, module_name):
|
| 46 |
"""Build the C extension from the provided codes."""
|
| 47 |
try: # Write the provided codes to their respective files.
|
| 48 |
_logger.info('WRITING GENERATED C CODE TO FILE...')
|
| 49 |
-
write_file(c_code, f"{module_name}.c")
|
| 50 |
_logger.info('WRITING GENERATED SETUP CODE TO FILE...')
|
| 51 |
-
write_file(setup_code, "setup.py")
|
| 52 |
except Exception as ex:
|
| 53 |
_logger.error(f"ERROR WHILE WRITING FILES:\n{ex}")
|
| 54 |
return f"An error occurred while writing files:\n{ex}"
|
| 55 |
try: # Build the extension and capture the output.
|
| 56 |
_logger.info('BUILDING THE EXTENSION...')
|
| 57 |
-
build_output = build_extension()
|
| 58 |
except Exception as ex: # If build fails, return the error message.
|
| 59 |
_logger.error(f"ERROR WHILE BUILDING EXTENSION:\n{ex}")
|
| 60 |
return str(ex)
|
|
|
|
| 10 |
|
| 11 |
|
| 12 |
# Define a function to write outputs to a file with a given filename.
|
| 13 |
+
def write_file(data, path):
|
| 14 |
"""Write data to a file with the specified filename."""
|
| 15 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 16 |
+
with open(path, "w") as file:
|
| 17 |
file.write(data)
|
| 18 |
|
| 19 |
|
| 20 |
# Extension compilation function.
|
| 21 |
+
def build_extension(compile_path):
|
| 22 |
"""Compile the C extension using 'setup.py' and return the compilation output."""
|
| 23 |
# Set default COMSPEC to cmd.exe on Windows to avoid issues with some C compilers.
|
| 24 |
if sys.platform == "win32":
|
|
|
|
| 28 |
try:
|
| 29 |
_logger.info('STARTING COMPILATION PROCESS...')
|
| 30 |
compile_cmd = ["python", "setup.py", "build_ext", "--inplace"]
|
| 31 |
+
compile_result = subprocess.run(compile_cmd, env=os.environ, cwd=compile_path,
|
| 32 |
check=True, text=True, capture_output=True)
|
| 33 |
except subprocess.CalledProcessError as ex:
|
| 34 |
_logger.error(f"COMPILATION FAILED WITH ERROR:\n{ex.stdout}\n{ex.stderr}")
|
|
|
|
| 43 |
|
| 44 |
|
| 45 |
# Extension compilation function.
|
| 46 |
+
def compile_extension(c_code, setup_code, module_name, compile_path):
|
| 47 |
"""Build the C extension from the provided codes."""
|
| 48 |
try: # Write the provided codes to their respective files.
|
| 49 |
_logger.info('WRITING GENERATED C CODE TO FILE...')
|
| 50 |
+
write_file(c_code, compile_path / f"{module_name}.c")
|
| 51 |
_logger.info('WRITING GENERATED SETUP CODE TO FILE...')
|
| 52 |
+
write_file(setup_code, compile_path / "setup.py")
|
| 53 |
except Exception as ex:
|
| 54 |
_logger.error(f"ERROR WHILE WRITING FILES:\n{ex}")
|
| 55 |
return f"An error occurred while writing files:\n{ex}"
|
| 56 |
try: # Build the extension and capture the output.
|
| 57 |
_logger.info('BUILDING THE EXTENSION...')
|
| 58 |
+
build_output = build_extension(compile_path)
|
| 59 |
except Exception as ex: # If build fails, return the error message.
|
| 60 |
_logger.error(f"ERROR WHILE BUILDING EXTENSION:\n{ex}")
|
| 61 |
return str(ex)
|
src/interface.py
CHANGED
|
@@ -1,16 +1,11 @@
|
|
| 1 |
"""Interface module for the AI Python C Extensions Generator application."""
|
| 2 |
|
| 3 |
-
from gradio import Blocks, Button, Dropdown, Examples, Markdown, Row,
|
|
|
|
| 4 |
|
| 5 |
from .examples import example_dict
|
| 6 |
|
| 7 |
|
| 8 |
-
css = """
|
| 9 |
-
.c_ext {background-color: #050;}
|
| 10 |
-
.python {background-color: #306998;}
|
| 11 |
-
"""
|
| 12 |
-
|
| 13 |
-
|
| 14 |
def _compile_extension():
|
| 15 |
return "COMPILE_EXTENSION PLACEHOLDER"
|
| 16 |
|
|
@@ -23,9 +18,11 @@ def get_interface(optimize_function, compile_stage=False,
|
|
| 23 |
compile_extension=_compile_extension,
|
| 24 |
test_extension=_test_extension,
|
| 25 |
default_platform="Windows",
|
|
|
|
| 26 |
models=["gpt-5.1-codex-mini"]):
|
| 27 |
"""Get the Gradio Blocks interface for the AI Python C Extensions Generator."""
|
| 28 |
-
with Blocks(
|
|
|
|
| 29 |
Markdown("## Convert code from Python to C Extension")
|
| 30 |
|
| 31 |
with Row():
|
|
@@ -57,7 +54,8 @@ def get_interface(optimize_function, compile_stage=False,
|
|
| 57 |
buttons=["copy"], elem_classes=["python"])
|
| 58 |
|
| 59 |
get_extension.click(optimize_function,
|
| 60 |
-
inputs=[python, module_name, platform,
|
|
|
|
| 61 |
outputs=[c_code, setup_code, usage_code])
|
| 62 |
|
| 63 |
# ######### BUILD AND TEST SECTIONS ######### #
|
|
@@ -75,11 +73,11 @@ def get_interface(optimize_function, compile_stage=False,
|
|
| 75 |
elem_classes=["python"])
|
| 76 |
|
| 77 |
compile_ext.click(compile_extension,
|
| 78 |
-
inputs=[c_code, setup_code, module_name],
|
| 79 |
outputs=[c_ext_out])
|
| 80 |
|
| 81 |
test_run.click(test_extension,
|
| 82 |
-
inputs=[usage_code],
|
| 83 |
outputs=[test_out])
|
| 84 |
|
| 85 |
return ui
|
|
|
|
| 1 |
"""Interface module for the AI Python C Extensions Generator application."""
|
| 2 |
|
| 3 |
+
from gradio import Blocks, Button, Dropdown, Examples, Markdown, Row, State, TextArea
|
| 4 |
+
from gradio import Textbox
|
| 5 |
|
| 6 |
from .examples import example_dict
|
| 7 |
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
def _compile_extension():
|
| 10 |
return "COMPILE_EXTENSION PLACEHOLDER"
|
| 11 |
|
|
|
|
| 18 |
compile_extension=_compile_extension,
|
| 19 |
test_extension=_test_extension,
|
| 20 |
default_platform="Windows",
|
| 21 |
+
compile_path="compile",
|
| 22 |
models=["gpt-5.1-codex-mini"]):
|
| 23 |
"""Get the Gradio Blocks interface for the AI Python C Extensions Generator."""
|
| 24 |
+
with Blocks(title="AI Python C Extensions Generator") as ui:
|
| 25 |
+
compile_path_st = State(value=compile_path)
|
| 26 |
Markdown("## Convert code from Python to C Extension")
|
| 27 |
|
| 28 |
with Row():
|
|
|
|
| 54 |
buttons=["copy"], elem_classes=["python"])
|
| 55 |
|
| 56 |
get_extension.click(optimize_function,
|
| 57 |
+
inputs=[python, module_name, platform,
|
| 58 |
+
compile_path_st, model],
|
| 59 |
outputs=[c_code, setup_code, usage_code])
|
| 60 |
|
| 61 |
# ######### BUILD AND TEST SECTIONS ######### #
|
|
|
|
| 73 |
elem_classes=["python"])
|
| 74 |
|
| 75 |
compile_ext.click(compile_extension,
|
| 76 |
+
inputs=[c_code, setup_code, module_name, compile_path_st],
|
| 77 |
outputs=[c_ext_out])
|
| 78 |
|
| 79 |
test_run.click(test_extension,
|
| 80 |
+
inputs=[usage_code, compile_path_st],
|
| 81 |
outputs=[test_out])
|
| 82 |
|
| 83 |
return ui
|
src/optimizer.py
CHANGED
|
@@ -4,7 +4,7 @@ from logging import getLogger
|
|
| 4 |
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
from openai import OpenAI
|
| 7 |
-
from pydantic import BaseModel
|
| 8 |
|
| 9 |
from .prompts import messages_for
|
| 10 |
|
|
@@ -24,9 +24,18 @@ _logger.info(f'INITIALIZED OPTIMIZER MODULE')
|
|
| 24 |
|
| 25 |
# Define Pydantic model class for GPT response parsing.
|
| 26 |
class _extension_codes(BaseModel):
|
| 27 |
-
c_code: str
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
def __str__(self):
|
| 32 |
"""Return a string representation of the optimization codes."""
|
|
@@ -38,13 +47,15 @@ class _extension_codes(BaseModel):
|
|
| 38 |
|
| 39 |
|
| 40 |
# Define optimization function using OpenAI's GPT model.
|
| 41 |
-
def optimize_gpt(python_code, module_name, platform, model=OPENAI_MODEL):
|
| 42 |
"""Generate an optimized C extension for Python."""
|
| 43 |
schema = _extension_codes.model_json_schema()
|
| 44 |
_logger.info('SENDING OPTIMIZATION REQUEST TO OPENAI... '
|
| 45 |
-
f'(MODEL: {model}, PLATFORM: {platform}
|
|
|
|
| 46 |
response = openai.responses.parse(
|
| 47 |
model=model, text_format=_extension_codes,
|
| 48 |
-
input=messages_for(python_code, module_name, schema, platform)
|
|
|
|
| 49 |
_logger.info('RECEIVED OPTIMIZATION RESPONSE FROM OPENAI')
|
| 50 |
return response.c_code, response.setup, response.usage
|
|
|
|
| 4 |
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
from openai import OpenAI
|
| 7 |
+
from pydantic import BaseModel, Field
|
| 8 |
|
| 9 |
from .prompts import messages_for
|
| 10 |
|
|
|
|
| 24 |
|
| 25 |
# Define Pydantic model class for GPT response parsing.
|
| 26 |
class _extension_codes(BaseModel):
|
| 27 |
+
c_code: str = Field(...,
|
| 28 |
+
description="Generated C extension source "
|
| 29 |
+
"code for the Python module.")
|
| 30 |
+
setup: str = Field(...,
|
| 31 |
+
description="The generated setup.py build script "
|
| 32 |
+
"used to compile the C extension module."
|
| 33 |
+
"It must assume the module is in the current folder "
|
| 34 |
+
"and not in any subfolder.")
|
| 35 |
+
usage: str = Field(...,
|
| 36 |
+
description="A Python usage example that imports the compiled "
|
| 37 |
+
"extension and compares its execution time with the "
|
| 38 |
+
"original Python implementation.")
|
| 39 |
|
| 40 |
def __str__(self):
|
| 41 |
"""Return a string representation of the optimization codes."""
|
|
|
|
| 47 |
|
| 48 |
|
| 49 |
# Define optimization function using OpenAI's GPT model.
|
| 50 |
+
def optimize_gpt(python_code, module_name, platform, compile_path, model=OPENAI_MODEL):
|
| 51 |
"""Generate an optimized C extension for Python."""
|
| 52 |
schema = _extension_codes.model_json_schema()
|
| 53 |
_logger.info('SENDING OPTIMIZATION REQUEST TO OPENAI... '
|
| 54 |
+
f'(MODEL: {model}, PLATFORM: {platform}, '
|
| 55 |
+
f'MODULE: {module_name}, COMPILE PATH: {compile_path})')
|
| 56 |
response = openai.responses.parse(
|
| 57 |
model=model, text_format=_extension_codes,
|
| 58 |
+
input=messages_for(python_code, module_name, schema, platform, compile_path)
|
| 59 |
+
).output_parsed
|
| 60 |
_logger.info('RECEIVED OPTIMIZATION RESPONSE FROM OPENAI')
|
| 61 |
return response.c_code, response.setup, response.usage
|
src/prompts.py
CHANGED
|
@@ -10,17 +10,20 @@ Your responses must always be a JSON with the following schema:
|
|
| 10 |
|
| 11 |
Use comments sparingly and do not provide any explanation other than occasional comments.
|
| 12 |
|
| 13 |
-
The C extension for Python needs to produce
|
| 14 |
time.
|
| 15 |
|
| 16 |
Make sure the C extension for Python code is correct and can be compiled with
|
| 17 |
'python setup.py build_ext' and used in Python.
|
| 18 |
|
| 19 |
The usage example must include a time measurement and a comparison with the original
|
| 20 |
-
Python code.
|
| 21 |
|
| 22 |
-
Do not include
|
|
|
|
|
|
|
| 23 |
|
|
|
|
| 24 |
Make sure the JSON is correctly formatted.
|
| 25 |
"""
|
| 26 |
|
|
@@ -36,6 +39,19 @@ a few code comments.
|
|
| 36 |
The module name, used to import, must be "{module_name}", the generated C file will
|
| 37 |
be named "{module_name}.c".
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
Pay attention to number types to ensure no int overflows.
|
| 40 |
Remember to #include all necessary C packages such as iomanip or <python.h>
|
| 41 |
|
|
@@ -52,10 +68,11 @@ Here is the Python code to reimplement:
|
|
| 52 |
|
| 53 |
|
| 54 |
# Define function to create the messages for the LLM.
|
| 55 |
-
def messages_for(python_code, module_name, schema, platform):
|
| 56 |
"""Create the messages given the Python code, module name, and platform."""
|
| 57 |
return [
|
| 58 |
{"role": "system", "content": system_message.format(schema=schema)},
|
| 59 |
{"role": "user", "content": user_prompt.format(python_code=python_code,
|
| 60 |
module_name=module_name,
|
|
|
|
| 61 |
platform=platform)}]
|
|
|
|
| 10 |
|
| 11 |
Use comments sparingly and do not provide any explanation other than occasional comments.
|
| 12 |
|
| 13 |
+
The C extension for Python needs to produce identical output in the fastest possible
|
| 14 |
time.
|
| 15 |
|
| 16 |
Make sure the C extension for Python code is correct and can be compiled with
|
| 17 |
'python setup.py build_ext' and used in Python.
|
| 18 |
|
| 19 |
The usage example must include a time measurement and a comparison with the original
|
| 20 |
+
Python code, absolute and proportional difference.
|
| 21 |
|
| 22 |
+
Do not include an `if __name__ == '__main__'` guard in the usage script.
|
| 23 |
+
The usage script must be importable, and the usage example must invoke its functions
|
| 24 |
+
directly.
|
| 25 |
|
| 26 |
+
Do not include any additional text or explanation outside the JSON structure.
|
| 27 |
Make sure the JSON is correctly formatted.
|
| 28 |
"""
|
| 29 |
|
|
|
|
| 39 |
The module name, used to import, must be "{module_name}", the generated C file will
|
| 40 |
be named "{module_name}.c".
|
| 41 |
|
| 42 |
+
The generated C extension module will be located in the "{compile_path}" folder.
|
| 43 |
+
Only the usage example file should import the module from that folder:
|
| 44 |
+
|
| 45 |
+
from {compile_path} import {module_name}
|
| 46 |
+
|
| 47 |
+
Do not use "{compile_path}" inside the generated C source or in the generated setup.py
|
| 48 |
+
file. The generated C source and setup.py must behave as if they are in the current
|
| 49 |
+
folder and must not reference any external path.
|
| 50 |
+
|
| 51 |
+
Do not include an `if __name__ == '__main__'` guard in the generated module or the
|
| 52 |
+
usage example. The usage script must be importable, and the usage example must invoke
|
| 53 |
+
its functions directly.
|
| 54 |
+
|
| 55 |
Pay attention to number types to ensure no int overflows.
|
| 56 |
Remember to #include all necessary C packages such as iomanip or <python.h>
|
| 57 |
|
|
|
|
| 68 |
|
| 69 |
|
| 70 |
# Define function to create the messages for the LLM.
|
| 71 |
+
def messages_for(python_code, module_name, schema, platform, compile_path):
|
| 72 |
"""Create the messages given the Python code, module name, and platform."""
|
| 73 |
return [
|
| 74 |
{"role": "system", "content": system_message.format(schema=schema)},
|
| 75 |
{"role": "user", "content": user_prompt.format(python_code=python_code,
|
| 76 |
module_name=module_name,
|
| 77 |
+
compile_path=compile_path,
|
| 78 |
platform=platform)}]
|
src/tester.py
CHANGED
|
@@ -9,9 +9,10 @@ _logger = getLogger(__name__)
|
|
| 9 |
|
| 10 |
|
| 11 |
# Define a function to write outputs to a file with a given filename.
|
| 12 |
-
def write_file(data,
|
| 13 |
"""Write data to a file with the specified filename."""
|
| 14 |
-
|
|
|
|
| 15 |
file.write(data)
|
| 16 |
|
| 17 |
|
|
@@ -25,6 +26,9 @@ def execute_python(code):
|
|
| 25 |
sys.stdout = output
|
| 26 |
# Execute the provided code in a clean global namespace.
|
| 27 |
exec(code, {})
|
|
|
|
|
|
|
|
|
|
| 28 |
finally:
|
| 29 |
# Restore original stdout.
|
| 30 |
sys.stdout = sys.__stdout__
|
|
@@ -32,10 +36,10 @@ def execute_python(code):
|
|
| 32 |
|
| 33 |
|
| 34 |
# Extension testing function.
|
| 35 |
-
def test_extension(usage_code):
|
| 36 |
"""Test the C extension executing the code and capture its output."""
|
| 37 |
try: # Write the code to file.
|
| 38 |
-
write_file(usage_code, "usage_example.py")
|
| 39 |
except Exception as ex:
|
| 40 |
return f"An error occurred while writing test file:\n{ex}"
|
| 41 |
try:
|
|
|
|
| 9 |
|
| 10 |
|
| 11 |
# Define a function to write outputs to a file with a given filename.
|
| 12 |
+
def write_file(data, path):
|
| 13 |
"""Write data to a file with the specified filename."""
|
| 14 |
+
path.parent.mkdir(parents=True, exist_ok=True)
|
| 15 |
+
with open(path, "w") as file:
|
| 16 |
file.write(data)
|
| 17 |
|
| 18 |
|
|
|
|
| 26 |
sys.stdout = output
|
| 27 |
# Execute the provided code in a clean global namespace.
|
| 28 |
exec(code, {})
|
| 29 |
+
except Exception as ex:
|
| 30 |
+
_logger.error(f"ERROR WHILE EXECUTING TEST CODE:\n{ex}")
|
| 31 |
+
return f"An error occurred while executing test code:\n{ex}"
|
| 32 |
finally:
|
| 33 |
# Restore original stdout.
|
| 34 |
sys.stdout = sys.__stdout__
|
|
|
|
| 36 |
|
| 37 |
|
| 38 |
# Extension testing function.
|
| 39 |
+
def test_extension(usage_code, compile_path):
|
| 40 |
"""Test the C extension executing the code and capture its output."""
|
| 41 |
try: # Write the code to file.
|
| 42 |
+
write_file(usage_code, compile_path / "usage_example.py")
|
| 43 |
except Exception as ex:
|
| 44 |
return f"An error occurred while writing test file:\n{ex}"
|
| 45 |
try:
|