8000 Add type hints to `Job` (#939) · tableau/server-client-python@7443f68 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7443f68

Browse files
authored
Add type hints to Job (#939)
1 parent c8170ae commit 7443f68

File tree

5 files changed

+81
-75
lines changed

5 files changed

+81
-75
lines changed

.github/workflows/slack.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ jobs:
99
name: Sends a message to Slack when a push, a pull request or an issue is made
1010
steps:
1111
- name: Send message to Slack API
12+
continue-on-error: true
1213
uses: archive/github-actions-slack@v2.2.2
1314
id: notify
1415
with:

tableauserverclient/models/job_item.py

Lines changed: 56 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,35 @@
33
from ..datetime_helpers import parse_datetime
44

55

6+
from typing import List, Optional, TYPE_CHECKING
7+
8+
if TYPE_CHECKING:
9+
import datetime
10+
11+
612
class JobItem(object):
713
class FinishCode:
814
"""
915
Status codes as documented on
1016
https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#query_job
1117
"""
1218

13-
Success = 0
14-
Failed = 1
15-
Cancelled = 2
19+
Success: int = 0
20+
Failed: int = 1
21+
Cancelled: int = 2
1622

1723
def __init__(
1824
self,
19-
id_,
20-
job_type,
21-
progress,
22-
created_at,
23-
started_at=None,
24-
completed_at=None,
25-
finish_code=0,
26-
notes=None,
27-
mode=None,
28-
flow_run=None,
25+
id_: str,
26+
job_type: str,
27+
progress: str,
28+
created_at: "datetime.datetime",
29+
started_at: Optional["datetime.datetime"] = None,
30+
completed_at: Optional["datetime.datetime"] = None,
31+
finish_code: int = 0,
32+
notes: Optional[List[str]] = None,
33+
mode: Optional[str] = None,
34+
flow_run: Optional[FlowRunItem] = None
2935
):
3036
self._id = id_
3137
self._type = job_type
@@ -34,48 +40,48 @@ def __init__(
3440
self._started_at = started_at
3541
self._completed_at = completed_at
3642
self._finish_code = finish_code
37-
self._notes = notes or []
43+
self._notes: List[str] = notes or []
3844
self._mode = mode
3945
self._flow_run = flow_run
4046

4147
@property
42-
def id(self):
48+
def id(self) -> str:
4349
return self._id
4450

4551
@property
46-
def type(self):
52+
def type(self) -> str:
4753
return self._type
4854

4955
@property
50-
def progress(self):
56+
def progress(self) -> str:
5157
return self._progress
5258

5359
@property
54-
def created_at(self):
60+
def created_at(self) -> "datetime.datetime":
5561
return self._created_at
5662

5763
@property
58-
def started_at(self):
64+
def started_at(self) -> Optional["datetime.datetime"]:
5965
return self._started_at
6066

6167
@property
62-
def completed_at(self):
68+
def completed_at(self) -> Optional["datetime.datetime"]:
6369
return self._completed_at
6470

6571
@property
66-
def finish_code(self):
72+
def finish_code(self) -> int:
6773
return self._finish_code
6874

6975
@property
70-
def notes(self):
76+
def notes(self) -> List[str]:
7177
return self._notes
7278

7379
@property
74-
def mode(self):
80+
def mode(self) -> Optional[str]:
7581
return self._mode
7682

7783
@mode.setter
78-
def mode(self, value):
84+
def mode(self, value: str) -> None:
7985
# check for valid data here
8086
self._mode = value
8187

@@ -94,7 +100,7 @@ def __repr__(self):
94100
)
95101

96102
@classmethod
97-
def from_response(cls, xml, ns):
103+
def from_response(cls, xml, ns) -> List["JobItem"]:
98104
parsed_response = ET.fromstring(xml)
99105
all_tasks_xml = parsed_response.findall(".//t:job", namespaces=ns)
100106

@@ -136,23 +142,23 @@ def _parse_element(cls, element, ns):
136142

137143
class BackgroundJobItem(object):
138144
class Status:
139-
Pending = "Pending"
140-
InProgress = "InProgress"
141-
Success = "Success"
142-
Failed = "Failed"
143-
Cancelled = "Cancelled"
145+
Pending: str = "Pending"
146+
InProgress: str = "InProgress"
147+
Success: str = "Success"
148+
Failed: str = "Failed"
149+
Cancelled: str = "Cancelled"
144150

145151
def __init__(
146152
self,
147-
id_,
148-
created_at,
149-
priority,
150-
job_type,
151-
status,
152-
title=None,
153-
subtitle=None,
154-
started_at=None,
155-
ended_at=None,
153+
id_: str,
154+
created_at: "datetime.datetime",
155+
priority: int,
156+
job_type: str,
157+
status: str,
158+
title: Optional[str] = None,
159+
subtitle: Optional[str] = None,
160+
started_at: Optional["datetime.datetime"] = None,
161+
ended_at: Optional["datetime.datetime"] = None,
156162
):
157163
self._id = id_
158164
self._type = job_type
@@ -165,49 +171,49 @@ def __init__(
165171
self._subtitle = subtitle
166172

167173
@property
168-
def id(self):
174+
def id(self) -> str:
169175
return self._id
170176

171177
@property
172-
def name(self):
178+
def name(self) -> Optional[str]:
173179
"""For API consistency - all other resource endpoints have a name attribute which is used to display what
174180
they are. Alias title as name to allow consistent handling of resources in the list sample."""
175181
return self._title
176182

177183
@property
178-
def status(self):
184+
def status(self) -> str:
179185
return self._status
180186

181187
@property
182-
def type(self):
188+
def type(self) -> str:
183189
return self._type
184190

185191
@property
186-
def created_at(self):
192+
def created_at(self) -> "datetime.datetime":
187193
return self._created_at
188194

189195
@property
190-
def started_at(self):
196+
def started_at(self) -> Optional["datetime.datetime"]:
191197
return self._started_at
192198

193199
@property
194-
def ended_at(self):
200+
def ended_at(self) -> Optional["datetime.datetime"]:
195201
return self._ended_at
196202

197203
@property
198-
def title(self):
204+
def title(self) -> Optional[str]:
199205
return self._title
200206

201207
@property
202 10000 -
def subtitle(self):
208+
def subtitle(self) -> Optional[str]:
203209
return self._subtitle
204210

205211
@property
206-
def priority(self):
212+
def priority(self) -> int:
207213
return self._priority
208214

209215
@classmethod
210-
def from_response(cls, xml, ns):
216+
def from_response(cls, xml, ns) -> List["BackgroundJobItem"]:
211217
parsed_response = ET.fromstring(xml)
212218
all_tasks_xml = parsed_response.findall(".//t:backgroundJob", namespaces=ns)
213219
return [cls._parse_element(x, ns) for x in all_tasks_xml]

tableauserverclient/models/workbook_item.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,7 @@
1212
import copy
1313
import uuid
1414

15-
from typing import (
16-
Dict,
17-
List,
18-
Optional,
19-
Set,
20-
TYPE_CHECKING,
21-
Union
22-
)
15+
from typing import Dict, List, Optional, Set, TYPE_CHECKING, Union
2316

2417
if TYPE_CHECKING:
2518
from .connection_item import ConnectionItem

tableauserverclient/server/endpoint/jobs_endpoint.py

Lin 10000 es changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@
88

99
logger = logging.getLogger("tableau.endpoint.jobs")
1010

11+
from typing import List, Optional, Tuple, TYPE_CHECKING, Union
12+
13+
1114

1215
class Jobs(Endpoint):
1316
@property
1417
def baseurl(self):
1518
return "{0}/sites/{1}/jobs".format(self.parent_srv.baseurl, self.parent_srv.site_id)
1619

1720
@api(version="2.6")
18-
def get(self, job_id=None, req_options=None):
21+
def get(
22+
self, job_id: Optional[str] = None, req_options: Optional[RequestOptionsBase] = None
23+
) -> Tuple[List[BackgroundJobItem], PaginationItem]:
1924
# Backwards Compatibility fix until we rev the major version
2025
if job_id is not None and isinstance(job_id, str):
2126
import warnings
@@ -32,21 +37,22 @@ def get(self, job_id=None, req_options=None):
3237
return jobs, pagination_item
3338

3439
@api(version="3.1")
35-
def cancel(self, job_id):
36-
id_ = getattr(job_id, "id", job_id)
37-
url = "{0}/{1}".format(self.baseurl, id_)
40+
def cancel(self, job_id: Union[str, JobItem]):
41+
if isinstance(job_id, JobItem):
42+
job_id = job_id.id
43+
assert isinstance(job_id, str)
44+
url = "{0}/{1}".format(self.baseurl, job_id)
3845
return self.put_request(url)
3946

4047
@api(version="2.6")
41-
def get_by_id(self, job_id):
48+
def get_by_id(self, job_id: str) -> JobItem:
4249
logger.info("Query for information about job " + job_id)
4350
url = "{0}/{1}".format(self.baseurl, job_id)
4451
server_response = self.get_request(url)
4552
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
4653
return new_job
4754

48-
@api(version="2.6")
49-
def wait_for_job(self, job_id, *, timeout=None):
55+
def wait_for_job(self, job_id: Union[str, JobItem], *, timeout: Optional[float] = None) -> JobItem:
5056
if isinstance(job_id, JobItem):
5157
job_id = job_id.id
5258
assert isinstance(job_id, str)

test/test_job.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818

1919
class JobTests(unittest.TestCase):
20-
def setUp(self):
20+
def setUp(self) -> None:
2121
self.server = TSC.Server('http://test')
2222
self.server.version = '3.1'
2323

@@ -27,7 +27,7 @@ def setUp(self):
2727

2828
self.baseurl = self.server.jobs.baseurl
2929

30-
def test_get(self):
30+
def test_get(self) -> None:
3131
response_xml = read_xml_asset(GET_XML)
3232
with requests_mock.mock() as m:
3333
m.get(self.baseurl, text=response_xml)
@@ -46,7 +46,7 @@ def test_get(self):
4646
self.assertEqual(started_at, job.started_at)
4747
self.assertEqual(ended_at, job.ended_at)
4848

49-
def test_get_by_id(self):
49+
def test_get_by_id(self) -> None:
5050
response_xml = read_xml_asset(GET_BY_ID_XML)
5151
job_id = '2eef4225-aa0c-41c4-8662-a76d89ed7336'
5252
with requests_mock.mock() as m:
@@ -56,26 +56,26 @@ def test_get_by_id(self):
5656
self.assertEqual(job_id, job.id)
5757
self.assertListEqual(job.notes, ['Job detail notes'])
5858

59-
def test_get_before_signin(self):
59+
def test_get_before_signin(self) -> None:
6060
self.server._auth_token = None
6161
self.assertRaises(TSC.NotSignedInError, self.server.jobs.get)
6262

63-
def test_cancel_id(self):
63+
def test_cancel_id(self) -> None:
6464
with requests_mock.mock() as m:
6565
m.put(self.baseurl + '/ee8c6e70-43b6-11e6-af4f-f7b0d8e20760', status_code=204)
6666
self.server.jobs.cancel('ee8c6e70-43b6-11e6-af4f-f7b0d8e20760')
6767

68-
def test_cancel_item(self):
68+
def test_cancel_item(self) -> None:
6969
created_at = datetime(2018, 5, 22, 13, 0, 29, tzinfo=utc)
7070
started_at = datetime(2018, 5, 22, 13, 0, 37, tzinfo=utc)
7171
job = TSC.JobItem('ee8c6e70-43b6-11e6-af4f-f7b0d8e20760', 'backgroundJob',
72-
0, created_at, started_at, None, 0)
72+
"0", created_at, started_at, None, 0)
7373
with requests_mock.mock() as m:
7474
m.put(self.baseurl + '/ee8c6e70-43b6-11e6-af4f-f7b0d8e20760', status_code=204)
7575
self.server.jobs.cancel(job)
7676

7777

78-
def test_wait_for_job_finished(self):
78+
def test_wait_for_job_finished(self) -> None:
7979
# Waiting for an already finished job, directly returns that job's info
8080
response_xml = read_xml_asset(GET_BY_ID_XML)
8181
job_id = '2eef4225-aa0c-41c4-8662-a76d89ed7336'
@@ -87,7 +87,7 @@ def test_wait_for_job_finished(self):
8787
self.assertListEqual(job.notes, ['Job detail notes'])
8888

8989

90-
def test_wait_for_job_failed(self):
90+
def test_wait_for_job_failed(self) -> None:
9191
# Waiting for a failed job raises an exception
9292
response_xml = read_xml_asset(GET_BY_ID_FAILED_XML)
9393
job_id = '77d5e57a-2517-479f-9a3c-a32025f2b64d'
@@ -97,7 +97,7 @@ def test_wait_for_job_failed(self):
9797
self.server.jobs.wait_for_job(job_id)
9898

9999

100-
def test_wait_for_job_timeout(self):
100+
def test_wait_for_job_timeout(self) -> None:
101101
# Waiting for a job which doesn't terminate will throw an exception
102102
response_xml = read_xml_asset(GET_BY_ID_INPROGRESS_XML)
103103
job_id = '77d5e57a-2517-479f-9a3c-a32025f2b64d'

0 commit comments

Comments
 (0)
0