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

130 lines
3.7 KiB
Python

"""
Function Helper Functions
"""
import logging
from prisma.enums import FunctionState
from prisma.models import CompiledRoute, ObjectType
from prisma.types import FunctionCreateInput, ObjectFieldCreateInput
from codex.common.model import (
FunctionDef,
ObjectTypeModel,
get_related_types,
normalize_type,
)
logger = logging.getLogger(__name__)
async def construct_function(
function: FunctionDef, available_types: dict[str, ObjectType]
) -> FunctionCreateInput:
if not function.function_template:
raise ValueError("Function template is required")
input = FunctionCreateInput(
functionName=function.name,
template=function.function_template or "",
description=function.function_desc,
state=FunctionState.WRITTEN
if function.is_implemented
else FunctionState.DEFINITION,
rawCode=function.function_code,
functionCode=function.function_code,
)
if function.return_type:
field = ObjectFieldCreateInput(
name="return",
description=function.return_desc,
typeName=normalize_type(function.return_type),
RelatedTypes={
"connect": [
{"id": type.id}
for type in get_related_types(function.return_type, available_types)
]
},
)
input["FunctionReturn"] = {"create": field}
if function.arg_types:
fields = [
ObjectFieldCreateInput(
name=name,
description=function.arg_descs.get(name, "-"),
typeName=normalize_type(type),
RelatedTypes={
"connect": [
{"id": type.id}
for type in get_related_types(type, available_types)
]
},
)
for name, type in function.arg_types
]
input["FunctionArgs"] = {"create": fields} # type: ignore
return input
def generate_object_code(obj: ObjectTypeModel) -> str:
if not obj.name:
return "" # Avoid generating an empty object
# Auto-generate a template for the object, this will not capture any class functions.
fields = f"\n{' ' * 8}".join(
[
f"{field.name}: {field.type} "
f"{('= '+field.value) if field.value else ''} "
f"{('# '+field.description) if field.description else ''}"
for field in obj.Fields or []
]
)
parent_class = ""
if obj.is_enum:
parent_class = "Enum"
elif obj.is_pydantic:
parent_class = "BaseModel"
doc_string = (
f"""\"\"\"
{obj.description}
\"\"\""""
if obj.description
else ""
)
method_body = ("\n" + " " * 8).join(obj.code.split("\n")) + "\n" if obj.code else ""
template = f"""
class {obj.name}({parent_class}):
{doc_string if doc_string else ""}
{fields if fields else ""}
{method_body if method_body else ""}
{"pass" if not fields and not method_body else ""}
"""
return "\n".join([line[4:] for line in template.split("\n")]).strip()
def generate_object_template(obj: ObjectType) -> str:
return generate_object_code(ObjectTypeModel(db_obj=obj))
def get_database_schema(compiled_route: CompiledRoute) -> str:
return (
"\n\n".join(
[
t.definition
for t in compiled_route.ApiRouteSpec.DatabaseSchema.DatabaseTables
]
)
if compiled_route
and compiled_route.ApiRouteSpec
and compiled_route.ApiRouteSpec.DatabaseSchema
and compiled_route.ApiRouteSpec.DatabaseSchema.DatabaseTables
else ""
)