diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index ed35439..fd8fec2 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -23,6 +23,7 @@ jobs: run: | black --diff --check tdworkflow tests isort --check-only tdworkflow tests + mypy --install-types --non-interactive tdworkflow - name: Test with pytest run: | pytest diff --git a/setup.cfg b/setup.cfg index 646b46d..fe637f0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,7 +33,7 @@ setup_requires = tdworkflow = py.typed [options.extras_require] -dev = black;flake8;isort;pytest;pytest-mock +dev = black;flake8;isort;pytest;pytest-mock;mypy doc = sphinx;sphinx_rtd_theme [flake8] diff --git a/tdworkflow/attempt.py b/tdworkflow/attempt.py index 1c7cb7d..e575a1b 100644 --- a/tdworkflow/attempt.py +++ b/tdworkflow/attempt.py @@ -1,6 +1,6 @@ import dataclasses from datetime import datetime -from typing import Dict +from typing import Any, Dict, Optional from .project import Project from .resource import Resource @@ -13,22 +13,22 @@ class Attempt(Resource): id: int sessionId: int = -1 sessionUuid: str = "" - sessionTime: datetime = None - workflow: Workflow = None - project: Project = None + sessionTime: Optional[datetime] = None + workflow: Optional[Workflow] = None + project: Optional[Project] = None index: int = -1 retryAttemptName: str = "" done: bool = False success: bool = False cancelRequested: bool = False - params: Dict = None - createdAt: datetime = None - finishedAt: datetime = None + params: Optional[Dict[str, Any]] = None + createdAt: Optional[datetime] = None + finishedAt: Optional[datetime] = None status: str = "" - def __post_init__(self): + def __post_init__(self) -> None: self.id = int(self.id) - self.sessionTime = parse_iso8601(self.sessionTime) + self.sessionTime = parse_iso8601(self.sessionTime) # type: ignore if self.project and isinstance(self.project, dict): self.project = Project(**self.project) if self.workflow and isinstance(self.workflow, dict): @@ -36,38 +36,38 @@ def __post_init__(self): self.done = bool(self.done) self.success = bool(self.success) self.cancelRequested = bool(self.cancelRequested) - self.createdAt = parse_iso8601(self.createdAt) - self.finishedAt = parse_iso8601(self.finishedAt) + self.createdAt = parse_iso8601(self.createdAt) # type: ignore + self.finishedAt = parse_iso8601(self.finishedAt) # type: ignore self.status = self.status @property - def session_id(self): + def session_id(self) -> int: return self.sessionId @property - def session_uuid(self): + def session_uuid(self) -> str: return self.sessionUuid @property - def session_time(self): + def session_time(self) -> Optional[datetime]: return self.sessionTime @property - def retry_attempt_name(self): + def retry_attempt_name(self) -> str: return self.retryAttemptName @property - def cancel_requested(self): + def cancel_requested(self) -> bool: return self.cancelRequested @property - def finished_at(self): + def finished_at(self) -> Optional[datetime]: return self.finishedAt - def finished(self): + def finished(self) -> bool: return bool(self.finished_at) - def update(self, **args): + def update(self, **args: Any) -> None: other_attempt = Attempt(**args) self.id = other_attempt.id self.sessionId = other_attempt.sessionId diff --git a/tdworkflow/client.py b/tdworkflow/client.py index 08431a9..f257fac 100644 --- a/tdworkflow/client.py +++ b/tdworkflow/client.py @@ -6,7 +6,7 @@ import time import uuid from datetime import datetime -from typing import Any, BinaryIO, Dict, List, Optional, Tuple, Union +from typing import Any, BinaryIO, Callable, Dict, List, Optional, Tuple, Union, cast import requests from requests.adapters import HTTPAdapter @@ -29,6 +29,8 @@ class WorkflowAPI: + get: Callable + def workflows( self, name_pattern: Optional[str] = None, @@ -52,7 +54,7 @@ def workflows( :return: List of Workflow :rtype: List[Workflow] """ - params = {} + params = {} # type: Dict[str, Union[str, bool, int, None]] if name_pattern: params["name_pattern"] = name_pattern if search_project_name: @@ -83,6 +85,10 @@ def workflow(self, workflow: Union[int, Workflow]) -> Workflow: class ProjectAPI: + get: Callable + put: Callable + delete: Callable + def project(self, project: Union[int, Project]) -> Project: """Get a project @@ -114,7 +120,7 @@ def projects( :return: List of Project :rtype: List[Project] """ - params = {} + params = {} # type: Dict[str, Union[str, int, None]] if name: params["name"] = name if name_pattern: @@ -147,7 +153,7 @@ def project_workflows( :return: List of Workflow :rtype: List[Workflow] """ - params = {} + params = {} # type: Dict[str, Union[str, int, None]] if workflow: workflow_name = ( workflow.name if isinstance(workflow, Workflow) else workflow @@ -277,7 +283,7 @@ def project_schedules( :param last_id: List schedules whose id is grater than this id for pagination :return: List of Schedule """ - params = {} + params = {} # type: Dict[str, Union[str, int, None]] if workflow: workflow_name = ( workflow.name if isinstance(workflow, Workflow) else workflow @@ -393,7 +399,7 @@ def project_sessions( :param page_size: Number of sessions to return :return: List of Session """ - params = {} + params = {} # type: Dict[str, Union[str, int, None]] if workflow: workflow_name = ( workflow.name if isinstance(workflow, Workflow) else workflow @@ -412,6 +418,10 @@ def project_sessions( class AttemptAPI: + get: Callable + put: Callable + post: Callable + def attempts( self, project: Optional[Union[str, Project]] = None, @@ -435,7 +445,7 @@ def attempts( :return: List of Attempt object :rtype: List[Attempt] """ - params = {} + params = {} # type: Dict[str, Union[str, bool, int, None]] if project: project_name = project.name if isinstance(project, Project) else project params.update({"project": project_name}) @@ -443,7 +453,7 @@ def attempts( workflow_name = ( workflow.name if isinstance(workflow, Workflow) else workflow ) - params.upadte({"workflow": workflow_name}) + params.update({"workflow": workflow_name}) if include_retried: params.update({"include_retried": include_retried}) if last_id: @@ -474,7 +484,11 @@ def attempt( raise ValueError(f"Unable to find attempt id {attempt_id}") if inplace: - attempt.update(**r) + if isinstance(attempt, int): + raise ValueError(f"Unable to use inplace with integer value {attempt=}") + else: + attempt.update(**r) + return None else: return Attempt.from_api_repr(**r) @@ -518,7 +532,9 @@ def start_attempt( :return: """ workflow_id = workflow.id if isinstance(workflow, Workflow) else workflow - _params = {"workflowId": workflow_id} + _params = { + "workflowId": workflow_id + } # type: Dict[str, Union[str, int, Dict, None]] workflow_params = workflow_params if workflow_params else {} _params.update({"params": workflow_params}) if not session_time: @@ -548,6 +564,7 @@ def kill_attempt( self.post(f"attempts/{attempt_id}/kill", content=True) if inplace: self.attempt(attempt, inplace=True) + return None else: return self.attempt(attempt) @@ -563,6 +580,9 @@ def wait_attempt( :return: Latest status of Attempt :rtype: Attempt """ + if isinstance(attempt, int): + attempt = cast(Attempt, self.attempt(attempt)) + while not attempt.done: time.sleep(wait_interval) self.attempt(attempt, inplace=True) @@ -571,6 +591,9 @@ def wait_attempt( class ScheduleAPI: + get: Callable + post: Callable + def schedules(self, last_id: Optional[int] = None) -> List[Schedule]: """List schedules @@ -614,7 +637,7 @@ def backfill_schedule( :param count: Count :return: ScheduleAttempt """ - params = {} + params = {} # type: Dict[str, Union[str, int, None]] if from_time: params["fromTime"] = to_iso8601(from_time) if attempt_name: @@ -673,7 +696,7 @@ def skip_schedule( :param dry_run: Flag for dry run :return: New Schedule """ - params = {} + params = {} # type: Dict[str, Union[str, datetime, bool, None]] if from_time: params["fromTime"] = to_iso8601(from_time) if next_time: @@ -690,6 +713,8 @@ def skip_schedule( class SessionAPI: + get: Callable + def sessions( self, last_id: Optional[int] = None, page_size: Optional[int] = None ) -> List[Session]: @@ -752,6 +777,8 @@ def session_attempts( class LogAPI: + get: Callable + def log_files( self, attempt: Union[Attempt, int], @@ -765,7 +792,7 @@ def log_files( :param direct_download: Flag for direct download :return: List of LogFile """ - params = {} + params = {} # type: Dict[str, Union[int, str, bool, None]] if task: params["task"] = task if direct_download: @@ -778,7 +805,9 @@ def log_files( else: return [] - def log_file(self, attempt: Union[Attempt, int], file: Union[LogFile, str]) -> str: + def log_file( + self, attempt: Union[Attempt, int], file: Union[LogFile, str] + ) -> Union[bytes, str]: """Get a log string for an attempt :param attempt: Target Attempt id or Attempt object @@ -796,7 +825,7 @@ def log_file(self, attempt: Union[Attempt, int], file: Union[LogFile, str]) -> s else: raise ValueError(f"Unable to get file: {file_name}") - def logs(self, attempt: Union[Attempt, int]) -> List[str]: + def logs(self, attempt: Union[Attempt, int]) -> List[Union[bytes, str]]: """Get log string list for an attempt :param attempt: Attempt ID or Attempt object @@ -900,7 +929,7 @@ def __init__( self.api_base = f"{scheme}://{self.endpoint}/api/" @property - def http(self): + def http(self) -> requests.Session: """ :return: Established session :rtype: requests.Session @@ -923,7 +952,7 @@ def get( """ url = f"{self.api_base}{path}" r = self.http.get(url, params=params) - logger.debug(f"{r.status_code}\n{r.content}") + logger.debug(f"{r.status_code!r}\n{r.content!r}") if not 200 <= r.status_code < 300: exceptions.raise_response_error(r) @@ -948,7 +977,7 @@ def post( """ url = f"{self.api_base}{path}" r = self.http.post(url, json=body) - logger.debug(f"{r.status_code}\n{r.content}") + logger.debug(f"{r.status_code!r}\n{r.content!r}") if not 200 <= r.status_code < 300: exceptions.raise_response_error(r) @@ -958,10 +987,12 @@ def post( elif r.content and "application/json" in r.headers.get("Content-Type", ""): return r.json() + return None + def put( self, path: str, - data: Optional[Union[Dict, List[Tuple], BinaryIO]] = None, + data: Optional[Union[str, Dict, List[Tuple], BinaryIO]] = None, _json: Optional[Dict[str, str]] = None, params: Optional[Dict[str, str]] = None, ) -> Optional[Dict[str, str]]: @@ -970,7 +1001,7 @@ def put( :param path: Treasure Workflow API path :type path: str :param data: Content body - :type data: Optional[Union[Dict, List[Tuple], BinaryIO]], oprional + :type data: Optional[str, Union[Dict, List[Tuple], BinaryIO]], optional :param _json: Content body as JSON :type _json: Optional[Dict[str, str]], optional :param params: Query parameters @@ -986,8 +1017,8 @@ def put( if not _json and data and hasattr(data, "read"): headers["Content-Type"] = "application/gzip" - r = self.http.put(url, data=data, headers=headers, params=params) - logger.debug(f"{r.status_code}\n{r.content}") + r = self.http.put(url, data=data, headers=headers, params=params) # type: ignore + logger.debug(f"{r.status_code!r}\n{r.content!r}") if not 200 <= r.status_code < 300: exceptions.raise_response_error(r) @@ -995,6 +1026,8 @@ def put( if r.content and "application/json" in r.headers.get("Content-Type", ""): return r.json() + return None + def delete( self, path: str, params: Optional[Dict[str, str]] = None ) -> Optional[Dict[str, str]]: @@ -1010,10 +1043,12 @@ def delete( url = f"{self.api_base}{path}" r = self.http.delete(url, params=params) - logger.debug(f"{r.status_code}\n{r.content}") + logger.debug(f"{r.status_code!r}\n{r.content!r}") if not 200 <= r.status_code < 300: exceptions.raise_response_error(r) if r.content and "application/json" in r.headers.get("Content-Type", ""): return r.json() + + return None diff --git a/tdworkflow/exceptions.py b/tdworkflow/exceptions.py index 520ce1a..53bb672 100644 --- a/tdworkflow/exceptions.py +++ b/tdworkflow/exceptions.py @@ -1,3 +1,5 @@ +from typing import NoReturn, Union + import requests @@ -5,9 +7,10 @@ class HttpError(Exception): pass -def raise_response_error(r: requests.Response): +def raise_response_error(r: requests.Response) -> Union[NoReturn, None]: try: r.raise_for_status() + return None except requests.exceptions.HTTPError as e: response = {} if r.content and "application/json" in r.headers.get("Content-Type", ""): diff --git a/tdworkflow/log.py b/tdworkflow/log.py index ea79ecf..bb2e674 100644 --- a/tdworkflow/log.py +++ b/tdworkflow/log.py @@ -1,6 +1,6 @@ import dataclasses from datetime import datetime -from typing import Dict +from typing import Any, Dict, Optional from .resource import Resource from .util import parse_iso8601 @@ -10,30 +10,30 @@ class LogFile(Resource): fileName: str taskName: str - direct: Dict + direct: Dict[Any, Any] fileSize: int agentId: str - fileTime: datetime = None + fileTime: Optional[datetime] = None - def __post_init__(self): - self.fileTime = parse_iso8601(self.fileTime) + def __post_init__(self) -> None: + self.fileTime = parse_iso8601(self.fileTime) # type: ignore @property - def file_name(self): + def file_name(self) -> str: return self.fileName @property - def taks_name(self): + def taks_name(self) -> str: return self.taskName @property - def file_time(self): + def file_time(self) -> Optional[datetime]: return self.fileTime @property - def file_size(self): + def file_size(self) -> int: return self.fileSize @property - def agent_id(self): + def agent_id(self) -> str: return self.agentId diff --git a/tdworkflow/project.py b/tdworkflow/project.py index 1c1b1f6..bba6742 100644 --- a/tdworkflow/project.py +++ b/tdworkflow/project.py @@ -1,5 +1,6 @@ import dataclasses from datetime import datetime +from typing import Optional from .resource import Resource from .util import parse_iso8601 @@ -12,32 +13,32 @@ class Project(Resource): revision: str = "" archiveType: str = "" archiveMd5: str = "" - createdAt: datetime = None - deletedAt: datetime = None - updatedAt: datetime = None + createdAt: Optional[datetime] = None + deletedAt: Optional[datetime] = None + updatedAt: Optional[datetime] = None - def __post_init__(self): + def __post_init__(self) -> None: self.id = int(self.id) - self.createdAt = parse_iso8601(self.createdAt) - self.deletedAt = parse_iso8601(self.deletedAt) - self.updatedAt = parse_iso8601(self.updatedAt) + self.createdAt = parse_iso8601(self.createdAt) # type: ignore + self.deletedAt = parse_iso8601(self.deletedAt) # type: ignore + self.updatedAt = parse_iso8601(self.updatedAt) # type: ignore @property - def archive_type(self): + def archive_type(self) -> str: return self.archiveType @property - def archive_md5(self): + def archive_md5(self) -> str: return self.archive_md5 @property - def created_at(self): + def created_at(self) -> Optional[datetime]: return self.createdAt @property - def deleted_at(self): + def deleted_at(self) -> Optional[datetime]: return self.deletedAt @property - def updated_at(self): + def updated_at(self) -> Optional[datetime]: return self.updatedAt diff --git a/tdworkflow/revision.py b/tdworkflow/revision.py index 225bde4..2db4c6b 100644 --- a/tdworkflow/revision.py +++ b/tdworkflow/revision.py @@ -1,6 +1,6 @@ import dataclasses from datetime import datetime -from typing import Dict +from typing import Any, Dict, Optional from .resource import Resource from .util import parse_iso8601 @@ -9,22 +9,22 @@ @dataclasses.dataclass class Revision(Resource): revision: str - createdAt: datetime = None + createdAt: Optional[datetime] = None archiveType: str = "" archiveMd5: str = "" - userInfo: Dict = None + userInfo: Optional[Dict[str, Any]] = None - def __post_init__(self): - self.createdAt = parse_iso8601(self.createdAt) + def __post_init__(self) -> None: + self.createdAt = parse_iso8601(self.createdAt) # type: ignore @property - def archive_type(self): + def archive_type(self) -> str: return self.archiveType @property - def archive_md5(self): + def archive_md5(self) -> str: return self.archive_md5 @property - def created_at(self): + def created_at(self) -> Optional[datetime]: return self.createdAt diff --git a/tdworkflow/schedule.py b/tdworkflow/schedule.py index 5d67e44..defdea4 100644 --- a/tdworkflow/schedule.py +++ b/tdworkflow/schedule.py @@ -1,6 +1,6 @@ import dataclasses from datetime import datetime -from typing import Dict, List +from typing import Any, Dict, List, Optional, Union from .attempt import Attempt from .project import Project @@ -8,45 +8,47 @@ from .util import parse_iso8601 from .workflow import Workflow +NextSchedule = Dict[str, Union[int, str, Dict[str, Any]]] + @dataclasses.dataclass class Schedule(Resource): id: int project: Project workflow: Workflow - createdAt: datetime = None - updatedAt: datetime = None - disabledAt: datetime = None - nextScheduleTime: Dict = None - nextRunTime: datetime = None + createdAt: Optional[datetime] = None + updatedAt: Optional[datetime] = None + disabledAt: Optional[datetime] = None + nextScheduleTime: Optional[NextSchedule] = None + nextRunTime: Optional[datetime] = None - def __post_init__(self): + def __post_init__(self) -> None: self.id = int(self.id) - self.project = Project(**self.project) - self.workflow = Workflow(**self.workflow) - self.createdAt = parse_iso8601(self.createdAt) - self.updatedAt = parse_iso8601(self.updatedAt) - self.disabledAt = parse_iso8601(self.disabledAt) - self.nextRunTime = parse_iso8601(self.nextRunTime) + self.project = Project(**self.project) # type: ignore + self.workflow = Workflow(**self.workflow) # type: ignore + self.createdAt = parse_iso8601(self.createdAt) # type: ignore + self.updatedAt = parse_iso8601(self.updatedAt) # type: ignore + self.disabledAt = parse_iso8601(self.disabledAt) # type: ignore + self.nextRunTime = parse_iso8601(self.nextRunTime) # type: ignore @property - def created_at(self): + def created_at(self) -> Optional[datetime]: return self.createdAt @property - def updated_at(self): + def updated_at(self) -> Optional[datetime]: return self.updatedAt @property - def disabled_at(self): + def disabled_at(self) -> Optional[datetime]: return self.disabledAt @property - def next_run_time(self): + def next_run_time(self) -> Optional[datetime]: return self.nextRunTime @property - def next_schedule_time(self): + def next_schedule_time(self) -> Optional[NextSchedule]: return self.nextScheduleTime @@ -54,11 +56,11 @@ def next_schedule_time(self): class ScheduleAttempt(Resource): id: int attempts: List[Attempt] - project: Project = None - workflow: Workflow = None + project: Optional[Project] = None + workflow: Optional[Workflow] = None - def __post_init__(self): + def __post_init__(self) -> None: self.id = int(self.id) - self.attempts = [Attempt(**att) for att in self.attempts] - self.project = Project(**self.project) - self.workflow = Project(**self.workflow) + self.attempts = [Attempt(**att) for att in self.attempts] # type: ignore + self.project = Project(**self.project) # type: ignore + self.workflow = Project(**self.workflow) # type: ignore diff --git a/tdworkflow/session.py b/tdworkflow/session.py index 37c587a..9bbc4bc 100644 --- a/tdworkflow/session.py +++ b/tdworkflow/session.py @@ -1,5 +1,6 @@ import dataclasses from datetime import datetime +from typing import Optional from .attempt import Attempt from .project import Project @@ -14,24 +15,24 @@ class Session(Resource): project: Project workflow: Workflow sessionUuid: str - sessionTime: datetime = None - lastAttempt: Attempt = None + sessionTime: Optional[datetime] = None + lastAttempt: Optional[Attempt] = None - def __post_init__(self): + def __post_init__(self) -> None: self.id = int(self.id) - self.project = Project(**self.project) - self.workflow = Workflow(**self.workflow) - self.sessionTime = parse_iso8601(self.sessionTime) - self.lastAttempt = Attempt(**self.lastAttempt) + self.project = Project(**self.project) # type: ignore + self.workflow = Workflow(**self.workflow) # type: ignore + self.sessionTime = parse_iso8601(self.sessionTime) # type: ignore + self.lastAttempt = Attempt(**self.lastAttempt) # type: ignore @property - def session_uuid(self): + def session_uuid(self) -> str: return self.sessionUuid @property - def session_time(self): + def session_time(self) -> Optional[datetime]: return self.session_time @property - def last_attempt(self): + def last_attempt(self) -> Optional[Attempt]: return self.lastAttempt diff --git a/tdworkflow/task.py b/tdworkflow/task.py index 93a22fa..a0fcd64 100644 --- a/tdworkflow/task.py +++ b/tdworkflow/task.py @@ -1,7 +1,7 @@ import dataclasses import json from datetime import datetime -from typing import Dict, List +from typing import Any, Dict, List, Optional from .resource import Resource from .util import parse_iso8601 @@ -11,66 +11,66 @@ class Task(Resource): id: int state: str - updatedAt: datetime = None + updatedAt: Optional[datetime] = None fullName: str = "" - parentId: int = None - upstreams: List[int] = None - retryAt: datetime = None - config: Dict = None - exportParams: Dict = None - storeParams: Dict = None - stateParams: Dict = None - error: Dict = None - startedAt: datetime = None + parentId: Optional[int] = None + upstreams: Optional[List[int]] = None + retryAt: Optional[datetime] = None + config: Optional[Dict[str, Any]] = None + exportParams: Optional[Dict[str, Any]] = None + storeParams: Optional[Dict[str, Any]] = None + stateParams: Optional[Dict[str, Any]] = None + error: Optional[Dict[str, Any]] = None + startedAt: Optional[datetime] = None cancelRequested: bool = False isGroup: bool = False - def __post_init__(self): + def __post_init__(self) -> None: self.id = int(self.id) - self.updatedAt = parse_iso8601(self.updatedAt) + self.updatedAt = parse_iso8601(self.updatedAt) # type: ignore self.parentId = int(self.parentId) if self.parentId else None - self.upstreams = [int(_id) for _id in self.upstreams] - self.retryAt = parse_iso8601(self.retryAt) - self.startedAt = parse_iso8601(self.startedAt) + self.upstreams = [int(_id) for _id in self.upstreams] if self.upstreams else [] + self.retryAt = parse_iso8601(self.retryAt) # type: ignore + self.startedAt = parse_iso8601(self.startedAt) # type: ignore @property - def updated_at(self): + def updated_at(self) -> Optional[datetime]: return self.updatedAt @property - def full_name(self): + def full_name(self) -> str: return self.fullName @property - def parent_id(self): + def parent_id(self) -> Optional[int]: return self.parentId @property - def retry_at(self): + def retry_at(self) -> Optional[datetime]: return self.retryAt @property - def export_params(self): + def export_params(self) -> Optional[Dict[str, Any]]: return self.exportParams @property - def store_params(self): + def store_params(self) -> Optional[Dict[str, Any]]: return self.storeParams @property - def state_params(self): + def state_params(self) -> Optional[Dict[str, Any]]: return self.stateParams @property - def started_at(self): + def started_at(self) -> Optional[datetime]: return self.startedAt @property - def cancel_requested(self): + def cancel_requested(self) -> bool: return self.cancelRequested @property - def group(self): + def group(self) -> bool: return self.isGroup diff --git a/tdworkflow/util.py b/tdworkflow/util.py index df09ba1..d9c64a6 100644 --- a/tdworkflow/util.py +++ b/tdworkflow/util.py @@ -5,7 +5,7 @@ import tarfile from datetime import datetime, timedelta, timezone from pathlib import Path -from typing import List, Optional +from typing import List, Optional, Union logger = logging.getLogger(__name__) @@ -34,15 +34,15 @@ def archive_files(target_dir: str, exclude_patterns: List[str]) -> io.BytesIO: return _bytes -def parse_iso8601(target: str) -> Optional[datetime]: +def parse_iso8601(target: Optional[str]) -> Optional[datetime]: if not target: return None return datetime.fromisoformat(target.replace("Z", "+00:00")) -def to_iso8601(dt: datetime) -> str: - if not datetime: +def to_iso8601(dt: Optional[Union[str, datetime]]) -> str: + if not dt: return "" if isinstance(dt, datetime): diff --git a/tdworkflow/workflow.py b/tdworkflow/workflow.py index 54ae76f..c530058 100644 --- a/tdworkflow/workflow.py +++ b/tdworkflow/workflow.py @@ -1,5 +1,6 @@ import dataclasses from datetime import datetime +from typing import Any, Dict, Optional from .project import Project from .resource import Resource @@ -10,29 +11,29 @@ class Workflow(Resource): id: int name: str - project: Project = None + project: Optional[Project] = None timezone: str = "" - config: dict = None + config: Optional[Dict[str, Any]] = None revision: str = "" - createdAt: datetime = None - deletedAt: datetime = None - updatedAt: datetime = None + createdAt: Optional[datetime] = None + deletedAt: Optional[datetime] = None + updatedAt: Optional[datetime] = None - def __post_init__(self): + def __post_init__(self) -> None: self.id = int(self.id) - self.project = Project(**self.project) if self.project else None - self.createdAt = parse_iso8601(self.createdAt) - self.deletedAt = parse_iso8601(self.deletedAt) - self.updatedAt = parse_iso8601(self.updatedAt) + self.project = Project(**self.project) if self.project else None # type: ignore + self.createdAt = parse_iso8601(self.createdAt) # type: ignore + self.deletedAt = parse_iso8601(self.deletedAt) # type: ignore + self.updatedAt = parse_iso8601(self.updatedAt) # type: ignore @property - def created_at(self): + def created_at(self) -> Optional[datetime]: return self.createdAt @property - def deleted_at(self): + def deleted_at(self) -> Optional[datetime]: return self.deletedAt @property - def updated_at(self): + def updated_at(self) -> Optional[datetime]: return self.updatedAt