Skip to content

Commit

Permalink
feat(restapi): add workflow for plugin task signature analysis
Browse files Browse the repository at this point in the history
This commit exposes the signature analysis module as a workflow in the Dioptra REST API. It updates
the Python client to support the new workflow. It also includes a new test suite the provides test
coverage for the workflow and client.
  • Loading branch information
jtsextonMITRE authored and keithmanville committed Feb 19, 2025
1 parent 27db81c commit 3b81da4
Show file tree
Hide file tree
Showing 15 changed files with 1,368 additions and 4 deletions.
20 changes: 20 additions & 0 deletions src/dioptra/client/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
T = TypeVar("T")

JOB_FILES_DOWNLOAD: Final[str] = "jobFilesDownload"
SIGNATURE_ANALYSIS: Final[str] = "pluginTaskSignatureAnalysis"


class WorkflowsCollectionClient(CollectionClient[T]):
Expand Down Expand Up @@ -86,3 +87,22 @@ def download_job_files(
return self._session.download(
self.url, JOB_FILES_DOWNLOAD, output_path=job_files_path, params=params
)

def analyze_plugin_task_signatures(self, python_code: str) -> T:
"""
Requests signature analysis for the functions in an annotated python file.
Args:
python_code: The contents of the python file.
filename: The name of the file.
Returns:
The response from the Dioptra API.
"""

return self._session.post(
self.url,
SIGNATURE_ANALYSIS,
json_={"pythonCode": python_code},
)
43 changes: 40 additions & 3 deletions src/dioptra/restapi/v1/workflows/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,19 @@

import structlog
from flask import request, send_file
from flask_accepts import accepts
from flask_accepts import accepts, responds
from flask_login import login_required
from flask_restx import Namespace, Resource
from injector import inject
from structlog.stdlib import BoundLogger

from .schema import FileTypes, JobFilesDownloadQueryParametersSchema
from .service import JobFilesDownloadService
from .schema import (
FileTypes,
JobFilesDownloadQueryParametersSchema,
SignatureAnalysisOutputSchema,
SignatureAnalysisSchema,
)
from .service import JobFilesDownloadService, SignatureAnalysisService

LOGGER: BoundLogger = structlog.stdlib.get_logger()

Expand Down Expand Up @@ -78,3 +83,35 @@ def get(self):
mimetype=mimetype[parsed_query_params["file_type"]],
download_name=download_name[parsed_query_params["file_type"]],
)


@api.route("/pluginTaskSignatureAnalysis")
class SignatureAnalysisEndpoint(Resource):
@inject
def __init__(
self, signature_analysis_service: SignatureAnalysisService, *args, **kwargs
) -> None:
"""Initialize the workflow resource.
All arguments are provided via dependency injection.
Args:
signature_analysis_service: A SignatureAnalysisService object.
"""
self._signature_analysis_service = signature_analysis_service
super().__init__(*args, **kwargs)

@login_required
@accepts(schema=SignatureAnalysisSchema, api=api)
@responds(schema=SignatureAnalysisOutputSchema, api=api)
def post(self):
"""Download a compressed file archive containing the files needed to execute a submitted job.""" # noqa: B950
log = LOGGER.new( # noqa: F841
request_id=str(uuid.uuid4()),
resource="SignatureAnalysis",
request_type="POST",
)
parsed_obj = request.parsed_obj
return self._signature_analysis_service.post(
python_code=parsed_obj["python_code"],
)
81 changes: 81 additions & 0 deletions src/dioptra/restapi/v1/workflows/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,84 @@ class JobFilesDownloadQueryParametersSchema(Schema):
by_value=True,
default=FileTypes.TAR_GZ.value,
)


class SignatureAnalysisSchema(Schema):

pythonCode = fields.String(
attribute="python_code",
metadata=dict(description="The contents of the python file"),
)


class SignatureAnalysisSignatureParamSchema(Schema):
name = fields.String(
attribute="name", metadata=dict(description="The name of the parameter")
)
type = fields.String(
attribute="type", metadata=dict(description="The type of the parameter")
)


class SignatureAnalysisSignatureInputSchema(SignatureAnalysisSignatureParamSchema):
required = fields.Boolean(
attribute="required",
metadata=dict(description="Whether this is a required parameter"),
)


class SignatureAnalysisSignatureOutputSchema(SignatureAnalysisSignatureParamSchema):
pass


class SignatureAnalysisSuggestedTypes(Schema):

# add proposed_type in next iteration

name = fields.String(
attribute="name",
metadata=dict(description="A suggestion for the name of the type"),
)

description = fields.String(
attribute="description",
metadata=dict(
description="The annotation the suggestion is attempting to represent"
),
)


class SignatureAnalysisSignatureSchema(Schema):
name = fields.String(
attribute="name", metadata=dict(description="The name of the function")
)
inputs = fields.Nested(
SignatureAnalysisSignatureInputSchema,
metadata=dict(description="A list of objects describing the input parameters."),
many=True,
)
outputs = fields.Nested(
SignatureAnalysisSignatureOutputSchema,
metadata=dict(
description="A list of objects describing the output parameters."
),
many=True,
)
missing_types = fields.Nested(
SignatureAnalysisSuggestedTypes,
metadata=dict(
description="A list of missing types for non-primitives defined by the file"
),
many=True,
)


class SignatureAnalysisOutputSchema(Schema):
tasks = fields.Nested(
SignatureAnalysisSignatureSchema,
metadata=dict(
description="A list of signature analyses for the plugin tasks "
"provided in the input file"
),
many=True,
)
52 changes: 51 additions & 1 deletion src/dioptra/restapi/v1/workflows/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
# ACCESS THE FULL CC BY 4.0 LICENSE HERE:
# https://creativecommons.org/licenses/by/4.0/legalcode
"""The server-side functions that perform workflows endpoint operations."""
from typing import IO, Final
from typing import IO, Any, Final

import structlog
from structlog.stdlib import BoundLogger

from dioptra.restapi.v1.shared.signature_analysis import get_plugin_signatures

from .lib import views
from .lib.package_job_files import package_job_files
from .schema import FileTypes
Expand Down Expand Up @@ -65,3 +67,51 @@ def get(self, job_id: int, file_type: FileTypes, **kwargs) -> IO[bytes]:
file_type=file_type,
logger=log,
)


class SignatureAnalysisService(object):
"""The service methods for performing signature analysis on a file."""

def post(self, python_code: str, **kwargs) -> dict[str, list[dict[str, Any]]]:
"""Perform signature analysis on a file.
Args:
filename: The name of the file.
python_code: The contents of the file.
Returns:
A dictionary containing the signature analysis.
"""
log: BoundLogger = kwargs.get("log", LOGGER.new())
log.debug(
"Performing signature analysis",
python_source=python_code,
)
endpoint_analyses = [
_create_endpoint_analysis_dict(signature)
for signature in get_plugin_signatures(python_source=python_code)
]
return {"tasks": endpoint_analyses}


def _create_endpoint_analysis_dict(
signature: dict[str, Any],
) -> dict[str, Any]:
"""Create an endpoint analysis dictionary from a signature analysis.
Args:
signature: The signature analysis.
Returns:
The endpoint analysis dictionary.
"""
return {
"name": signature["name"],
"inputs": signature["inputs"],
"outputs": signature["outputs"],
"missing_types": [
{
"description": suggested_type["type_annotation"],
"name": suggested_type["suggestion"],
}
for suggested_type in signature["suggested_types"]
],
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This Software (Dioptra) is being made available as a public service by the
# National Institute of Standards and Technology (NIST), an Agency of the United
# States Department of Commerce. This software was developed in part by employees of
# NIST and in part by NIST contractors. Copyright in portions of this software that
# were developed by NIST contractors has been licensed or assigned to NIST. Pursuant
# to Title 17 United States Code Section 105, works of NIST employees are not
# subject to copyright protection in the United States. However, NIST may hold
# international copyright in software created by its employees and domestic
# copyright (or licensing rights) in portions of software that were assigned or
# licensed to NIST. To the extent that NIST holds copyright in this software, it is
# being made available under the Creative Commons Attribution 4.0 International
# license (CC BY 4.0). The disclaimers of the CC BY 4.0 license apply to all parts
# of the software developed or licensed by NIST.
#
# ACCESS THE FULL CC BY 4.0 LICENSE HERE:
# https://creativecommons.org/licenses/by/4.0/legalcode
import dioptra.pyplugs as foo


@foo.register
def test_plugin():
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This Software (Dioptra) is being made available as a public service by the
# National Institute of Standards and Technology (NIST), an Agency of the United
# States Department of Commerce. This software was developed in part by employees of
# NIST and in part by NIST contractors. Copyright in portions of this software that
# were developed by NIST contractors has been licensed or assigned to NIST. Pursuant
# to Title 17 United States Code Section 105, works of NIST employees are not
# subject to copyright protection in the United States. However, NIST may hold
# international copyright in software created by its employees and domestic
# copyright (or licensing rights) in portions of software that were assigned or
# licensed to NIST. To the extent that NIST holds copyright in this software, it is
# being made available under the Creative Commons Attribution 4.0 International
# license (CC BY 4.0). The disclaimers of the CC BY 4.0 license apply to all parts
# of the software developed or licensed by NIST.
#
# ACCESS THE FULL CC BY 4.0 LICENSE HERE:
# https://creativecommons.org/licenses/by/4.0/legalcode
import dioptra.pyplugs


@dioptra.pyplugs.register()
def the_plugin(arg1: Optional[str]) -> Union[int, bool]:
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This Software (Dioptra) is being made available as a public service by the
# National Institute of Standards and Technology (NIST), an Agency of the United
# States Department of Commerce. This software was developed in part by employees of
# NIST and in part by NIST contractors. Copyright in portions of this software that
# were developed by NIST contractors has been licensed or assigned to NIST. Pursuant
# to Title 17 United States Code Section 105, works of NIST employees are not
# subject to copyright protection in the United States. However, NIST may hold
# international copyright in software created by its employees and domestic
# copyright (or licensing rights) in portions of software that were assigned or
# licensed to NIST. To the extent that NIST holds copyright in this software, it is
# being made available under the Creative Commons Attribution 4.0 International
# license (CC BY 4.0). The disclaimers of the CC BY 4.0 license apply to all parts
# of the software developed or licensed by NIST.
#
# ACCESS THE FULL CC BY 4.0 LICENSE HERE:
# https://creativecommons.org/licenses/by/4.0/legalcode
import dioptra.pyplugs


@dioptra.pyplugs.register
def plugin_func(arg1: foo(2)) -> foo(2):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This Software (Dioptra) is being made available as a public service by the
# National Institute of Standards and Technology (NIST), an Agency of the United
# States Department of Commerce. This software was developed in part by employees of
# NIST and in part by NIST contractors. Copyright in portions of this software that
# were developed by NIST contractors has been licensed or assigned to NIST. Pursuant
# to Title 17 United States Code Section 105, works of NIST employees are not
# subject to copyright protection in the United States. However, NIST may hold
# international copyright in software created by its employees and domestic
# copyright (or licensing rights) in portions of software that were assigned or
# licensed to NIST. To the extent that NIST holds copyright in this software, it is
# being made available under the Creative Commons Attribution 4.0 International
# license (CC BY 4.0). The disclaimers of the CC BY 4.0 license apply to all parts
# of the software developed or licensed by NIST.
#
# ACCESS THE FULL CC BY 4.0 LICENSE HERE:
# https://creativecommons.org/licenses/by/4.0/legalcode
from dioptra.pyplugs import register


@register
def my_plugin() -> None:
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This Software (Dioptra) is being made available as a public service by the
# National Institute of Standards and Technology (NIST), an Agency of the United
# States Department of Commerce. This software was developed in part by employees of
# NIST and in part by NIST contractors. Copyright in portions of this software that
# were developed by NIST contractors has been licensed or assigned to NIST. Pursuant
# to Title 17 United States Code Section 105, works of NIST employees are not
# subject to copyright protection in the United States. However, NIST may hold
# international copyright in software created by its employees and domestic
# copyright (or licensing rights) in portions of software that were assigned or
# licensed to NIST. To the extent that NIST holds copyright in this software, it is
# being made available under the Creative Commons Attribution 4.0 International
# license (CC BY 4.0). The disclaimers of the CC BY 4.0 license apply to all parts
# of the software developed or licensed by NIST.
#
# ACCESS THE FULL CC BY 4.0 LICENSE HERE:
# https://creativecommons.org/licenses/by/4.0/legalcode
from dioptra import pyplugs


@pyplugs.register()
def do_things(arg1: Optional[str], arg2: int = 123):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This Software (Dioptra) is being made available as a public service by the
# National Institute of Standards and Technology (NIST), an Agency of the United
# States Department of Commerce. This software was developed in part by employees of
# NIST and in part by NIST contractors. Copyright in portions of this software that
# were developed by NIST contractors has been licensed or assigned to NIST. Pursuant
# to Title 17 United States Code Section 105, works of NIST employees are not
# subject to copyright protection in the United States. However, NIST may hold
# international copyright in software created by its employees and domestic
# copyright (or licensing rights) in portions of software that were assigned or
# licensed to NIST. To the extent that NIST holds copyright in this software, it is
# being made available under the Creative Commons Attribution 4.0 International
# license (CC BY 4.0). The disclaimers of the CC BY 4.0 license apply to all parts
# of the software developed or licensed by NIST.
#
# ACCESS THE FULL CC BY 4.0 LICENSE HERE:
# https://creativecommons.org/licenses/by/4.0/legalcode
from dioptra import pyplugs as foo


@foo.register
def test_plugin():
pass
Loading

0 comments on commit 3b81da4

Please sign in to comment.