Skip to content

Commit 7e9cd33

Browse files
authored
Merge branch 'development' into PermissionItem-add-WebAuthoringForFlows
2 parents 7d3679f + 7bd23f4 commit 7e9cd33

File tree

12 files changed

+2140
-1918
lines changed

12 files changed

+2140
-1918
lines changed

.github/workflows/publish-pypi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
fetch-depth: 0
2020
- uses: actions/setup-python@v5
2121
with:
22-
python-version: 3.9
22+
python-version: 3.13
2323
- name: Build dist files
2424
run: |
2525
python -m pip install --upgrade pip

.github/workflows/run-tests.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
fail-fast: false
1414
matrix:
1515
os: [ubuntu-latest, macos-latest, windows-latest]
16-
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
16+
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14', '3.14t']
1717

1818
runs-on: ${{ matrix.os }}
1919

@@ -38,6 +38,7 @@ jobs:
3838
uses: actions/setup-python@v5
3939
with:
4040
python-version: ${{ matrix.python-version }}
41+
allow-prereleases: ${{ matrix.allow-prereleases || false }}
4142

4243
- name: Install dependencies
4344
run: |

tableauserverclient/models/datasource_item.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ def _set_values(
490490
self._owner = owner
491491

492492
@classmethod
493-
def from_response(cls, resp: str, ns: dict) -> list["DatasourceItem"]:
493+
def from_response(cls, resp: bytes, ns: dict) -> list["DatasourceItem"]:
494494
all_datasource_items = list()
495495
parsed_response = fromstring(resp)
496496
all_datasource_xml = parsed_response.findall(".//t:datasource", namespaces=ns)

tableauserverclient/server/endpoint/datasources_endpoint.py

Lines changed: 94 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
from contextlib import closing
88
from pathlib import Path
9-
from typing import Literal, Optional, TYPE_CHECKING, Union, overload
10-
from collections.abc import Iterable, Mapping, Sequence
9+
from typing import Literal, Optional, TYPE_CHECKING, TypedDict, TypeVar, Union, overload
10+
from collections.abc import Iterable, Sequence
1111

1212
from tableauserverclient.helpers.headers import fix_filename
1313
from tableauserverclient.models.dqw_item import DQWItem
@@ -50,13 +50,50 @@
5050
FileObject = Union[io.BufferedReader, io.BytesIO]
5151
PathOrFile = Union[FilePath, FileObject]
5252

53-
FilePath = Union[str, os.PathLike]
5453
FileObjectR = Union[io.BufferedReader, io.BytesIO]
5554
FileObjectW = Union[io.BufferedWriter, io.BytesIO]
5655
PathOrFileR = Union[FilePath, FileObjectR]
5756
PathOrFileW = Union[FilePath, FileObjectW]
5857

5958

59+
HyperActionCondition = TypedDict(
60+
"HyperActionCondition",
61+
{
62+
"op": str,
63+
"target-col": str,
64+
"source-col": str,
65+
},
66+
)
67+
68+
HyperActionRow = TypedDict(
69+
"HyperActionRow",
70+
{
71+
"action": Literal[
72+
"update",
73+
"upsert",
74+
"delete",
75+
],
76+
"source-table": str,
77+
"target-table": str,
78+
"condition": HyperActionCondition,
79+
},
80+
)
81+
82+
HyperActionTable = TypedDict(
83+
"HyperActionTable",
84+
{
85+
"action": Literal[
86+
"insert",
87+
"replace",
88+
],
89+
"source-table": str,
90+
"target-table": str,
91+
},
92+
)
93+
94+
HyperAction = Union[HyperActionTable, HyperActionRow]
95+
96+
6097
class Datasources(QuerysetEndpoint[DatasourceItem], TaggingMixin[DatasourceItem]):
6198
def __init__(self, parent_srv: "Server") -> None:
6299
super().__init__(parent_srv)
@@ -191,16 +228,34 @@ def delete(self, datasource_id: str) -> None:
191228
self.delete_request(url)
192229
logger.info(f"Deleted single datasource (ID: {datasource_id})")
193230

231+
T = TypeVar("T", bound=FileObjectW)
232+
233+
@overload
234+
def download(
235+
self,
236+
datasource_id: str,
237+
filepath: T,
238+
include_extract: bool = True,
239+
) -> T: ...
240+
241+
@overload
242+
def download(
243+
self,
244+
datasource_id: str,
245+
filepath: Optional[FilePath] = None,
246+
include_extract: bool = True,
247+
) -> str: ...
248+
194249
# Download 1 datasource by id
195250
@api(version="2.0")
196251
@parameter_added_in(no_extract="2.5")
197252
@parameter_added_in(include_extract="2.5")
198253
def download(
199254
self,
200-
datasource_id: str,
201-
filepath: Optional[PathOrFileW] = None,
202-
include_extract: bool = True,
203-
) -> PathOrFileW:
255+
datasource_id,
256+
filepath=None,
257+
include_extract=True,
258+
):
204259
"""
205260
Downloads the specified data source from a site. The data source is
206261
downloaded as a .tdsx file.
@@ -479,13 +534,13 @@ def publish(
479534
@parameter_added_in(as_job="3.0")
480535
def publish(
481536
self,
482-
datasource_item: DatasourceItem,
483-
file: PathOrFileR,
484-
mode: str,
485-
connection_credentials: Optional[ConnectionCredentials] = None,
486-
connections: Optional[Sequence[ConnectionItem]] = None,
487-
as_job: bool = False,
488-
) -> Union[DatasourceItem, JobItem]:
537+
datasource_item,
538+
file,
539+
mode,
540+
connection_credentials=None,
541+
connections=None,
542+
as_job=False,
543+
):
489544
"""
490545
Publishes a data source to a server, or appends data to an existing
491546
data source.
@@ -631,7 +686,7 @@ def update_hyper_data(
631686
datasource_or_connection_item: Union[DatasourceItem, ConnectionItem, str],
632687
*,
633688
request_id: str,
634-
actions: Sequence[Mapping],
689+
actions: Sequence[HyperAction],
635690
payload: Optional[FilePath] = None,
636691
) -> JobItem:
637692
"""
@@ -898,15 +953,35 @@ def _get_datasource_revisions(
898953
revisions = RevisionItem.from_response(server_response.content, self.parent_srv.namespace, datasource_item)
899954
return revisions
900955

901-
# Download 1 datasource revision by revision number
902-
@api(version="2.3")
956+
T = TypeVar("T", bound=FileObjectW)
957+
958+
@overload
959+
def download_revision(
960+
self,
961+
datasource_id: str,
962+
revision_number: Optional[str],
963+
filepath: T,
964+
include_extract: bool = True,
965+
) -> T: ...
966+
967+
@overload
903968
def download_revision(
904969
self,
905970
datasource_id: str,
906971
revision_number: Optional[str],
907-
filepath: Optional[PathOrFileW] = None,
972+
filepath: Optional[FilePath] = None,
908973
include_extract: bool = True,
909-
) -> PathOrFileW:
974+
) -> str: ...
975+
976+
# Download 1 datasource revision by revision number
977+
@api(version="2.3")
978+
def download_revision(
979+
self,
980+
datasource_id,
981+
revision_number,
982+
filepath=None,
983+
include_extract=True,
984+
):
910985
"""
911986
Downloads a specific version of a data source prior to the current one
912987
in .tdsx format. To download the current version of a data source set

tableauserverclient/server/endpoint/workbooks_endpoint.py

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@
3030
from tableauserverclient.server import RequestFactory
3131

3232
from typing import (
33+
Literal,
3334
Optional,
3435
TYPE_CHECKING,
36+
TypeVar,
3537
Union,
38+
overload,
3639
)
3740
from collections.abc import Iterable, Sequence
3841

@@ -383,16 +386,34 @@ def update_connections(
383386
logger.info(f"Updated connections for workbook {workbook_item.id}: {', '.join(updated_ids)}")
384387
return connection_items
385388

389+
T = TypeVar("T", bound=FileObjectW)
390+
391+
@overload
392+
def download(
393+
self,
394+
workbook_id: str,
395+
filepath: T,
396+
include_extract: bool = True,
397+
) -> T: ...
398+
399+
@overload
400+
def download(
401+
self,
402+
workbook_id: str,
403+
filepath: Optional[FilePath] = None,
404+
include_extract: bool = True,
405+
) -> str: ...
406+
386407
# Download workbook contents with option of passing in filepath
387408
@api(version="2.0")
388409
@parameter_added_in(no_extract="2.5")
389410
@parameter_added_in(include_extract="2.5")
390411
def download(
391412
self,
392-
workbook_id: str,
393-
filepath: Optional[PathOrFileW] = None,
394-
include_extract: bool = True,
395-
) -> PathOrFileW:
413+
workbook_id,
414+
filepath=None,
415+
include_extract=True,
416+
):
396417
"""
397418
Downloads a workbook to the specified directory (optional).
398419
@@ -741,6 +762,30 @@ def delete_permission(self, item: WorkbookItem, capability_item: PermissionsRule
741762
"""
742763
return self._permissions.delete(item, capability_item)
743764

765+
@overload
766+
def publish(
767+
self,
768+
workbook_item: WorkbookItem,
769+
file: PathOrFileR,
770+
mode: str,
771+
connections: Optional[Sequence[ConnectionItem]],
772+
as_job: Literal[False],
773+
skip_connection_check: bool,
774+
parameters=None,
775+
) -> WorkbookItem: ...
776+
777+
@overload
778+
def publish(
779+
self,
780+
workbook_item: WorkbookItem,
781+
file: PathOrFileR,
782+
mode: str,
783+
connections: Optional[Sequence[ConnectionItem]],
784+
as_job: Literal[True],
785+
skip_connection_check: bool,
786+
parameters=None,
787+
) -> JobItem: ...
788+
744789
@api(version="2.0")
745790
@parameter_added_in(as_job="3.0")
746791
@parameter_added_in(connections="2.8")
@@ -977,15 +1022,27 @@ def _get_workbook_revisions(
9771022
revisions = RevisionItem.from_response(server_response.content, self.parent_srv.namespace, workbook_item)
9781023
return revisions
9791024

1025+
T = TypeVar("T", bound=FileObjectW)
1026+
1027+
@overload
1028+
def download_revision(
1029+
self, workbook_id: str, revision_number: Optional[str], filepath: T, include_extract: bool
1030+
) -> T: ...
1031+
1032+
@overload
1033+
def download_revision(
1034+
self, workbook_id: str, revision_number: Optional[str], filepath: Optional[FilePath], include_extract: bool
1035+
) -> str: ...
1036+
9801037
# Download 1 workbook revision by revision number
9811038
@api(version="2.3")
9821039
def download_revision(
9831040
self,
984-
workbook_id: str,
985-
revision_number: Optional[str],
986-
filepath: Optional[PathOrFileW] = None,
987-
include_extract: bool = True,
988-
) -> PathOrFileW:
1041+
workbook_id,
1042+
revision_number,
1043+
filepath,
1044+
include_extract=True,
1045+
):
9891046
"""
9901047
Downloads a workbook revision to the specified directory (optional).
9911048

tableauserverclient/server/request_factory.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,7 @@ def batch_create(self, element: ET.Element, tags: set[str], content: content_typ
924924
if item.id is None:
925925
raise ValueError(f"Item {item} must have an ID to be tagged.")
926926
content_element.attrib["id"] = item.id
927+
content_element.attrib["contentType"] = item.__class__.__name__.replace("Item", "")
927928

928929
return ET.tostring(element)
929930

test/assets/favorites_add_view.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@
1111
<project id="5241e88d-d384-4fd7-9c2f-648b5247efc5"/>
1212
<tags />
1313
</view>
14-
</tsResponse>
14+
</favorite>
15+
</favorites>
16+
</tsResponse>

0 commit comments

Comments
 (0)