8000 Merge branch 'development' into jorwoods/fix_pager_typing · Der-Henning/server-client-python@bb4ba5a · GitHub
[go: up one dir, main page]

Skip to content

Commit bb4ba5a

Browse files
authored
Merge branch 'development' into jorwoods/fix_pager_typing
2 parents a7b5e2c + 678d46a commit bb4ba5a

10 files changed

+130
-54
lines changed

tableauserverclient/server/endpoint/datasources_endpoint.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
PathOrFileW = Union[FilePath, FileObjectW]
5656

5757

58-
class Datasources(QuerysetEndpoint[DatasourceItem], TaggingMixin):
58+
class Datasources(QuerysetEndpoint[DatasourceItem], TaggingMixin[DatasourceItem]):
5959
def __init__(self, parent_srv: "Server") -> None:
6060
super(Datasources, self).__init__(parent_srv)
6161
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
@@ -126,7 +126,7 @@ def download(
126126
datasource_id: str,
127127
filepath: Optional[PathOrFileW] = None,
128128
include_extract: bool = True,
129-
) -> str:
129+
) -> PathOrFileW:
130130
return self.download_revision(
131131
datasource_id,
132132
None,
@@ -405,7 +405,7 @@ def _get_datasource_revisions(
405405
def download_revision(
406406
self,
407407
datasource_id: str,
408-
revision_number: str,
408+
revision_number: Optional[str],
409409
filepath: Optional[PathOrFileW] = None,
410410
include_extract: bool = True,
411411
) -> PathOrFileW:

tableauserverclient/server/endpoint/endpoint.py

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,41 @@
1+
from typing_extensions import Concatenate, ParamSpec
12
from tableauserverclient import datetime_helpers as datetime
23

34
import abc
45
from packaging.version import Version
56
from functools import wraps
67
from xml.etree.ElementTree import ParseError
7-
from typing import Any, Callable, Dict, Generic, List, Optional, TYPE_CHECKING, Tuple, TypeVar, Union
8+
from typing import (
9+
Any,
10+
Callable,
11+
Dict,
12+
Generic,
13+
List,
14+
Optional,
15+
TYPE_CHECKING,
16+
Tuple,
17+
TypeVar,
18+
Union,
19+
)
820

921
from tableauserverclient.models.pagination_item import PaginationItem
1022
from tableauserverclient.server.request_options import RequestOptions
1123

12-
from .exceptions import (
24+
from tableauserverclient.server.endpoint.exceptions import (
1325
ServerResponseError,
1426
InternalServerError,
1527
NonXMLResponseError,
1628
NotSignedInError,
1729
)
18-
from ..exceptions import EndpointUnavailableError
30+
from tableauserverclient.server.exceptions import EndpointUnavailableError
1931

2032
from tableauserverclient.server.query import QuerySet
2133
from tableauserverclient import helpers, get_versions
2234

2335
from tableauserverclient.helpers.logging import logger
24-
from tableauserverclient.config import DELAY_SLEEP_SECONDS
2536

2637
if TYPE_CHECKING:
27-
from ..server import Server
38+
from tableauserverclient.server.server import Server
2839
from requests import Response
2940

3041

@@ -38,7 +49,7 @@
3849
USER_AGENT_HEADER = "User-Agent"
3950

4051

41-
class Endpoint(object):
52+
class Endpoint:
4253
def __init__(self, parent_srv: "Server"):
4354
self.parent_srv = parent_srv
4455

@@ -232,7 +243,12 @@ def patch_request(self, url, xml_request, content_type=XML_CONTENT_TYPE, paramet
232243
)
233244

234245

235-
def api(version):
246+
E = TypeVar("E", bound="Endpoint")
247+
P = ParamSpec("P")
248+
R = TypeVar("R")
249+
250+
251+
def api(version: str) -> Callable[[Callable[Concatenate[E, P], R]], Callable[Concatenate[E, P], R]]:
236252
"""Annotate the minimum supported version for an endpoint.
237253
238254
Checks the version on the server object and compares normalized versions.
@@ -251,9 +267,9 @@ def api(version):
251267
>>> ...
252268
"""
253269

254-
def _decorator(func):
270+
def _decorator(func: Callable[Concatenate[E, P], R]) -> Callable[Concatenate[E, P], R]:
255271
@wraps(func)
256-
def wrapper(self, *args, **kwargs):
272+
def wrapper(self: E, *args: P.args, **kwargs: P.kwargs) -> R:
257273
self.parent_srv.assert_at_least_version(version, self.__class__.__name__)
258274
return func(self, *args, **kwargs)
259275

@@ -262,7 +278,7 @@ def wrapper(self, *args, **kwargs):
262278
return _decorator
263279

264280

265-
def parameter_added_in(**params):
281+
def parameter_added_in(**params: str) -> Callable[[Callable[Concatenate[E, P], R]], Callable[Concatenate[E, P], R]]:
266282
"""Annotate minimum versions for new parameters or request options on an endpoint.
267283
268284
The api decorator documents when an endpoint was added, this decorator annotates
@@ -285,9 +301,9 @@ def parameter_added_in(**params):
285301
>>> ...
286302
"""
287303

288-
def _decorator(func):
304+
def _decorator(func: Callable[Concatenate[E, P], R]) -> Callable[Concatenate[E, P], R]:
289305
@wraps(func)
290-
def wrapper(self, *args, **kwargs):
306+
def wrapper(self: E, *args: P.args, **kwargs: P.kwargs) -> R:
291307
import warnings
292308

293309
server_ver = Version(self.parent_srv.version or "0.0")
@@ -335,5 +351,5 @@ def paginate(self, **kwargs) -> QuerySet[T]:
335351
return queryset
336352

337353
@abc.abstractmethod
338-
def get(self, request_options: RequestOptions) -> Tuple[List[T], PaginationItem]:
354+
def get(self, request_options: Optional[RequestOptions] = None) -> Tuple[List[T], PaginationItem]:
339355
raise NotImplementedError(f".get has not been implemented for {self.__class__.__qualname__}")

tableauserverclient/server/endpoint/flows_endpoint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
PathOrFileW = Union[FilePath, FileObjectW]
5252

5353

54-
class Flows(QuerysetEndpoint[FlowItem], TaggingMixin):
54+
class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
5555
def __init__(self, parent_srv):
5656
super(Flows, self).__init__(parent_srv)
5757
self._resource_tagger = _ResourceTagger(parent_srv)

tableauserverclient/server/endpoint/jobs_endpoint.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from typing_extensions import Self, overload
23

34
from tableauserverclient.server.query import QuerySet
45

@@ -13,15 +14,25 @@
1314
from typing import List, Optional, Tuple, Union
1415

1516

16-
class Jobs(QuerysetEndpoint[JobItem]):
17+
class Jobs(QuerysetEndpoint[BackgroundJobItem]):
1718
@property
1819
def baseurl(self):
1920
return "{0}/sites/{1}/jobs".format(self.parent_srv.baseurl, self.parent_srv.site_id)
2021

22+
@overload # type: ignore[override]
23+
def get(self: Self, job_id: str, req_options: Optional[RequestOptionsBase] = None) -> JobItem: # type: ignore[override]
24+
...
25+
26+
@overload # type: ignore[override]
27+
def get(self: Self, job_id: RequestOptionsBase, req_options: None) -> Tuple[List[BackgroundJobItem], PaginationItem]: # type: ignore[override]
28+
...
29+
30+
@overload # type: ignore[override]
31+
def get(self: Self, job_id: None, req_options: Optional[RequestOptionsBase]) -> Tuple[List[BackgroundJobItem], PaginationItem]: # type: ignore[override]
32+
...
33+
2134
@api(version="2.6")
22-
def get(
23-
self, job_id: Optional[str] = None, req_options: Optional[RequestOptionsBase] = None
24-
) -> Tuple[List[BackgroundJobItem], PaginationItem]:
35+
def get(self, job_id=None, req_options=None):
2536
# Backwards Compatibility fix until we rev the major version
2637
if job_id is not None and isinstance(job_id, str):
2738
import warnings
@@ -77,7 +88,7 @@ def wait_for_job(self, job_id: Union[str, JobItem], *, timeout: Optional[float]
7788
else:
7889
raise AssertionError("Unexpected finish_code in job", job)
7990

80-
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[JobItem]:
91+
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[BackgroundJobItem]:
8192
"""
8293
Queries the Tableau Server for items using the specified filters. Page
8394
size can be specified to limit the number of items returned in a single

tableauserverclient/server/endpoint/resource_tagger.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import abc
22
import copy
3-
from typing import Iterable, Optional, Protocol, Set, Union, TYPE_CHECKING, runtime_checkable
3+
from typing import Generic, Iterable, Optional, Protocol, Set, TypeVar, Union, TYPE_CHECKING, runtime_checkable
44
import urllib.parse
55

66
from tableauserverclient.server.endpoint.endpoint import Endpoint, api
@@ -62,27 +62,24 @@ def update_tags(self, baseurl, resource_item):
6262
logger.info("Updated tags to {0}".format(resource_item.tags))
6363

6464

65-
class HasID(Protocol):
66-
@property
67-
def id(self) -> Optional[str]:
68-
pass
65+
class Response(Protocol):
66+
content: bytes
6967

7068

7169
@runtime_checkable
7270
class Taggable(Protocol):
73-
_initial_tags: Set[str]
7471
tags: Set[str]
72+
_initial_tags: Set[str]
7573

7674
@property
7775
def id(self) -> Optional[str]:
7876
pass
7977

8078

81-
class Response(Protocol):
82-
content: bytes
79+
T = TypeVar("T")
8380

8481

85-
class TaggingMixin(abc.ABC):
82+
class TaggingMixin(abc.ABC, Generic[T]):
8683
parent_srv: "Server"
8784

8885
@property
@@ -98,7 +95,7 @@ def put_request(self, url, request) -> Response:
9895
def delete_request(self, url) -> None:
9996
pass
10097

101-
def add_tags(self, item: Union[HasID, Taggable, str], tags: Union[Iterable[str], str]) -> Set[str]:
98+
def add_tags(self, item: Union[T, str], tags: Union[Iterable[str], str]) -> Set[str]:
10299
item_id = getattr(item, "id", item)
103100

104101
if not isinstance(item_id, str):
@@ -114,7 +111,7 @@ def add_tags(self, item: Union[HasID, Taggable, str], tags: Union[Iterable[str],
114111
server_response = self.put_request(url, add_req)
115112
return TagItem.from_response(server_response.content, self.parent_srv.namespace)
116113

117-
def delete_tags(self, item: Union[HasID, Taggable, str], tags: Union[Iterable[str], str]) -> None:
114+
def delete_tags(self, item: Union[T, str], tags: Union[Iterable[str], str]) -> None:
118115
item_id = getattr(item, "id", item)
119116

120117
if not isinstance(item_id, str):
@@ -130,17 +127,23 @@ def delete_tags(self, item: Union[HasID, Taggable, str], tags: Union[Iterable[st
130127
url = f"{self.baseurl}/{item_id}/tags/{encoded_tag_name}"
131128
self.delete_request(url)
132129

133-
def update_tags(self, item: Taggable) -> None:
134-
if item.tags == item._initial_tags:
130+
def update_tags(self, item: T) -> None:
131+
if (initial_tags := getattr(item, "_initial_tags", None)) is None:
132+
raise ValueError(f"{item} does not have initial tags.")
133+
if (tags := getattr(item, "tags", None)) is None:
134+
raise ValueError(f"{item} does not have tags.")
135+
if tags == initial_tags:
135136
return
136137

137-
add_set = item.tags - item._initial_tags
138-
remove_set = item._initial_tags - item.tags
138+
add_set = tags - initial_tags
139+
remove_set = initial_tags - tags
139140
self.delete_tags(item, remove_set)
140141
if add_set:
141-
item.tags = self.add_tags(item, add_set)
142-
item._initial_tags = copy.copy(item.tags)
143-
logger.info(f"Updated tags to {item.tags}")
142+
tags = self.add_tags(item, add_set)
143+
setattr(item, "tags", tags)
144+
145+
setattr(item, "_initial_tags", copy.copy(tags))
146+
logger.info(f"Updated tags to {tags}")
144147

145148

146149
content = Iterable[Union["ColumnItem", "DatabaseItem", "DatasourceItem", "FlowItem", "TableItem", "Work 10000 bookItem"]]

tableauserverclient/server/endpoint/tables_endpoint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from tableauserverclient.helpers.logging import logger
1414

1515

16-
class Tables(Endpoint, TaggingMixin):
16+
class Tables(Endpoint, TaggingMixin[TableItem]):
1717
def __init__(self, parent_srv):
1818
super(Tables, self).__init__(parent_srv)
1919

tableauserverclient/server/endpoint/views_endpoint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
)
2424

2525

26-
class Views(QuerysetEndpoint[ViewItem], TaggingMixin):
26+
class Views(QuerysetEndpoint[ViewItem], TaggingMixin[ViewItem]):
2727
def __init__(self, parent_srv):
2828
super(Views, self).__init__(parent_srv)
2929
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)

tableauserverclient/server/endpoint/workbooks_endpoint.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
PathOrFileW = Union[FilePath, FileObjectW]
6060

6161

62-
class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin):
62+
class Workbooks(QuerysetEndpoint[WorkbookItem], TaggingMixin[WorkbookItem]):
6363
def __init__(self, parent_srv: "Server") -> None:
6464
super(Workbooks, self).__init__(parent_srv)
6565
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
@@ -184,7 +184,7 @@ def download(
184184
workbook_id: str,
185185
filepath: Optional[PathOrFileW] = None,
186186
include_extract: bool = True,
187-
) -> str:
187+
) -> PathOrFileW:
188188
return self.download_revision(
189189
workbook_id,
190190
None,

0 commit comments

Comments
 (0)
0