Files
d379a0c363 [AGPT-571] Integrate PyRight (#141)
Integrating PyRight project-based code-linting. This will allow us to detect:
* Misuse of async functions.
* Misuse of optional type and other annotated types (currently disabled)
* Wrong usage of pydantic and prisma model.
* Wrong usage of third-party library.
* Use of undeclared field
* Use of uninstalled third-party library (which will prompt LLM to add the requirement list)

Known trade-off:

Setting-up PyRight requires building a full-project (generate the Prisma model code, installing 3rd-party library, compiling the function with the object dependency) this slows down the code-validation during the code-validation step.

---------

Co-authored-by: majdyz <[email protected]>
Co-authored-by: SwiftyOS <[email protected]>
2024-03-22 16:51:47 +04:00

125 lines
3.6 KiB
Python

from typing import List
from prisma.models import Function
from prisma.models import Function as FunctionDBModel
from prisma.models import ObjectType
from pydantic import BaseModel
from codex.common.model import FunctionDef
from codex.common.model import ObjectTypeModel as ObjectDef
from codex.develop.function import generate_object_code, generate_object_template
class Package(BaseModel):
package_name: str
version: str | None = None
specifier: str | None = None
def __str__(self):
if self.version:
return f"{self.package_name}{self.specifier}{self.version}"
else:
return self.package_name
class GeneratedFunctionResponse(BaseModel):
function_id: str | None = None
function_name: str
compiled_route_id: str
available_objects: dict[str, ObjectType]
available_functions: dict[str, Function]
template: str
rawCode: str
packages: List[Package]
imports: List[str]
functionCode: str
functions: List[FunctionDef]
objects: List[ObjectDef]
db_schema: str
def get_compiled_code(self) -> str:
return "\n".join(self.imports) + "\n\n" + self.rawCode.strip()
def regenerate_compiled_code(self) -> str:
"""
Regenerate imports & raw code using the available objects and functions.
"""
imports = self.imports.copy()
for obj in self.available_objects.values():
imports.extend(obj.importStatements)
self.imports = sorted(set(imports))
def __append_comment(code_block: str, comment="noqa") -> str:
"""
Append `# noqa` to the first line of the code block.
This is to suppress flake8 warnings for redefined names.
"""
lines = code_block.split("\n")
lines[0] = lines[0] + " # " + comment
return "\n".join(lines)
def __generate_stub(name, is_enum):
if not name:
return ""
elif is_enum:
return f"class {name}(Enum):\n pass"
else:
return f"class {name}(BaseModel):\n pass"
object_stubs_code = "\n\n".join(
[
__generate_stub(obj.name, obj.isEnum)
for obj in self.available_objects.values()
]
+ [
__generate_stub(obj.name, obj.is_enum)
for obj in self.objects
if obj.name not in self.available_objects
]
)
objects_code = "\n\n".join(
[
__append_comment(generate_object_template(obj))
for obj in self.available_objects.values()
]
+ [
__append_comment(generate_object_code(obj))
for obj in self.objects
if obj.name not in self.available_objects
]
)
functions_code = "\n\n".join(
[
f.template.strip()
for f in self.available_functions.values()
if f.functionName != self.function_name
]
+ [
f.function_template.strip()
for f in self.functions
if f.name not in self.available_functions and f.function_template
]
)
self.rawCode = (
object_stubs_code.strip()
+ "\n\n"
+ objects_code.strip()
+ "\n\n"
+ functions_code.strip()
+ "\n\n"
+ self.functionCode.strip()
)
return self.get_compiled_code()
class ApplicationGraphs(BaseModel):
code_graphs: List[FunctionDBModel]