diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index 113c7e6..4365257 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -25,6 +25,8 @@ jobs: run: flake8 pro_tes/ setup.py - name: Lint with Pylint run: pylint pro_tes/ setup.py + - name: Type check with mypy + run: mypy pro_tes/ test: name: Run tests runs-on: ubuntu-latest diff --git a/pro_tes/ga4gh/tes/models.py b/pro_tes/ga4gh/tes/models.py index 4d8d3b6..0380b19 100644 --- a/pro_tes/ga4gh/tes/models.py +++ b/pro_tes/ga4gh/tes/models.py @@ -300,7 +300,7 @@ class ServiceType(CustomBaseModel): ), example="org.ga4gh", ) - artifact: str = Field( + artifact: Enum = Field( ..., description=( "Name of the API or GA4GH specification implemented. " @@ -349,7 +349,7 @@ class Service(CustomBaseModel): description="Name of this service. Should be human readable.", example="My project", ) - type: ServiceType + type: Optional[ServiceType] description: Optional[str] = Field( None, description=( @@ -601,6 +601,7 @@ class TesTask(CustomBaseModel): ), example={"WORKFLOW_ID": "cwl-01234", "PROJECT_GROUP": "alice-lab"}, ) + logs: Optional[list[TesTaskLog]] = Field( None, description=( @@ -708,8 +709,8 @@ class DbDocument(CustomBaseModel): tes_endpoint: External TES endpoint. """ - task: TesTask = TesTask() - task_original: TesTask = TesTask(executors=[]) + task: TesTask = TesTask() # type: ignore + task_original: TesTask = TesTask(executors=[]) # type: ignore user_id: Optional[str] = None worker_id: str = "" basic_auth: BasicAuth = BasicAuth() diff --git a/pro_tes/ga4gh/tes/service_info.py b/pro_tes/ga4gh/tes/service_info.py index a9c1fc0..f9a24c8 100644 --- a/pro_tes/ga4gh/tes/service_info.py +++ b/pro_tes/ga4gh/tes/service_info.py @@ -24,7 +24,7 @@ class ServiceInfo: def __init__(self) -> None: """Construct class instance.""" self.db_client: Collection = ( - current_app.config.foca.db.dbs["taskStore"] + current_app.config.foca.db.dbs["taskStore"] # type: ignore .collections["service_info"] .client ) @@ -65,7 +65,7 @@ def init_service_info_from_config(self) -> None: Set service info only if it does not yet exist. """ - service_info_conf = current_app.config.foca.serviceInfo + service_info_conf = current_app.config.foca.serviceInfo # type: ignore try: service_info_db = self.get_service_info() except NotFound: diff --git a/pro_tes/ga4gh/tes/task_runs.py b/pro_tes/ga4gh/tes/task_runs.py index 8bfccc3..37c4b28 100644 --- a/pro_tes/ga4gh/tes/task_runs.py +++ b/pro_tes/ga4gh/tes/task_runs.py @@ -57,7 +57,7 @@ class TaskRuns: def __init__(self) -> None: """Construct object instance.""" - self.foca_config: Config = current_app.config.foca + self.foca_config: Config = current_app.config.foca # type: ignore self.db_client: Collection = ( self.foca_config.db.dbs["taskStore"].collections["tasks"].client ) @@ -84,7 +84,9 @@ def create_task( # pylint: disable=too-many-statements,too-many-branches # apply middlewares mw_handler = MiddlewareHandler() - mw_handler.set_middlewares(paths=current_app.config.foca.middlewares) + mw_handler.set_middlewares( + paths=current_app.config.foca.middlewares # type: ignore + ) logger.debug(f"Middlewares registered: {mw_handler.middlewares}") request_modified = mw_handler.apply_middlewares(request=request) @@ -276,7 +278,7 @@ def list_tasks(self, **kwargs) -> dict: view = kwargs.get("view", "BASIC") projection = self._set_projection(view=view) - name_prefix: str = kwargs.get("name_prefix") + name_prefix: str = str(kwargs.get("name_prefix")) if name_prefix is not None: filter_dict["task_original.name"] = {"$regex": f"^{name_prefix}"} @@ -355,7 +357,7 @@ def cancel_task(self, id: str, **kwargs) -> dict: if document is None: logger.error(f"task '{id}' not found.") raise TaskNotFound - db_document = DbDocument(**document) + db_document: DbDocument = DbDocument(**document) if db_document.task.state in States.CANCELABLE: db_connector = DbDocumentConnector( @@ -366,10 +368,17 @@ def cancel_task(self, id: str, **kwargs) -> dict: f"{db_document.tes_endpoint.host.rstrip('/')}/" f"{db_document.tes_endpoint.base_path.strip('/')}" ) - if self.store_logs: - task_id = db_document.task.logs[0].metadata.forwarded_to.id + + assert db_document.task.logs is not None + logs: list[TesTaskLog] = db_document.task.logs + + if (self.store_logs and logs and logs[0].metadata and + logs[0].metadata.forwarded_to): + task_id = logs[0].metadata.forwarded_to.id + task_id = logs[0].metadata.forwarded_to.id else: - task_id = db_document.task.logs[0].metadata["remote_task_id"] + task_id = logs[0].metadata["remote_task_id"] # type: ignore + logger.info( "Trying to cancel task with task identifier" f" '{task_id}' and worker job" @@ -421,6 +430,7 @@ def _write_doc_to_db( except DuplicateKeyError: continue assert document is not None + assert document.task.id is not None return document.task.id, document.worker_id raise DuplicateKeyError("Could not insert document into database.") @@ -603,10 +613,13 @@ def _update_task_metadata( Returns: The updated database document. """ - for logs in db_document.task.logs: + assert db_document.task.logs is not None + logs: list[TesTaskLog] = db_document.task.logs + for log in logs: + assert log.metadata is not None tesNextTes_obj = TesNextTes(id=remote_task_id, url=tes_url) - if logs.metadata.forwarded_to is None: - logs.metadata.forwarded_to = tesNextTes_obj + if log.metadata.forwarded_to is None: + log.metadata.forwarded_to = tesNextTes_obj return db_document @staticmethod diff --git a/pro_tes/tasks/track_task_progress.py b/pro_tes/tasks/track_task_progress.py index 3173515..09b6fa0 100644 --- a/pro_tes/tasks/track_task_progress.py +++ b/pro_tes/tasks/track_task_progress.py @@ -50,7 +50,7 @@ def task__track_task_progress( # pylint: disable=too-many-arguments user: User-name for basic authentication. password: Password for basic authentication. """ - foca_config: Config = current_app.config.foca + foca_config: Config = current_app.config.foca # type: ignore controller_config: dict = foca_config.controllers["post_task"] # create database client @@ -110,6 +110,8 @@ def task__track_task_progress( # pylint: disable=too-many-arguments # updating task after task is finished document.task.state = task_converted.state + assert task_converted.logs is not None + assert document.task.logs is not None for index, logs in enumerate(task_converted.logs): document.task.logs[index].logs = logs.logs document.task.logs[index].outputs = logs.outputs diff --git a/requirements_dev.txt b/requirements_dev.txt index f6f1bfe..6674972 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -7,3 +7,5 @@ mypy>=0.990 pylint>=2.15.5 pytest>=7.2.0 python-semantic-release>=7.32.2 +mypy>=1.8.0 +types-python-dateutil>=2.8.19.20240106 \ No newline at end of file