Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
a481299
Silence property decorator warnings
jorwoods Feb 2, 2021
5209c1f
Remove refs to distutils2
jorwoods Feb 2, 2021
2028935
Ignore problem variable in _version.py
jorwoods Feb 2, 2021
ddd84f6
Linting changes
jorwoods Feb 3, 2021
3a8faeb
Mypy checking in pipeline
jorwoods Feb 3, 2021
43c8dd7
Mypy ignore imports
jorwoods Feb 3, 2021
db1de41
Install mypy in pipeline
jorwoods Feb 3, 2021
ac9b06b
Fix mypy errors in datasource_item
jorwoods Feb 3, 2021
9a17beb
Add pre-commit config
jorwoods Feb 3, 2021
9be8bd7
Add pre-commit config
jorwoods Feb 3, 2021
13bde24
Type hint on datasources_endpoint
jorwoods Feb 3, 2021
5ea3e17
update_permissions takes a list of rules
jorwoods Feb 3, 2021
d4315de
Run mypy on tests as well
jorwoods Feb 3, 2021
0acf44c
Add mypy test and samples to travis
jorwoods Feb 3, 2021
45df4ff
Make type hint imports conditional
jorwoods Feb 4, 2021
bac2811
Annotate workbookitem init
jorwoods Feb 4, 2021
e19b28d
Continue typing workbookitem
jorwoods Feb 4, 2021
2a77b28
Begin annotations on workbook endpoint
jorwoods Feb 4, 2021
7efb7a6
Squashed commit of the following:
jorwoods Feb 18, 2021
0bf6fd9
Ignore property decorator warnings
jorwoods Feb 18, 2021
a8b42e1
Continue annotations on wb endpoint
jorwoods Feb 18, 2021
973237d
More workbook endpoint
jorwoods Feb 18, 2021
9a81506
Type hinting. Hinting error in publish
jorwoods Feb 18, 2021
cd3342d
Formatting
jorwoods Feb 18, 2021
cc5e9dc
Stop testing 3.5
jorwoods Feb 18, 2021
8870a07
Remove failing typecheck
jorwoods Feb 21, 2021
a8e182f
Python 3.5 is deprecated
jorwoods Feb 21, 2021
ee29429
Resolve development conflicts
jorwoods Feb 21, 2021
7ff1ad4
Remove misc check from pre-commit
jorwoods Feb 24, 2021
505f0e2
Remove ignore comments
jorwoods Feb 25, 2021
24574cb
Clean up function signatures
jorwoods Feb 25, 2021
4fa4c54
Fix PathLike call
jorwoods Feb 25, 2021
662145b
Explicitly return None
jorwoods Feb 25, 2021
f2e5a72
Move mode to separate line
jorwoods Feb 25, 2021
b146fdd
Remove bad TypeVar
jorwoods Mar 10, 2021
afa05ef
Merge branch 'development' into jorwoods/mypy
Apr 30, 2021
6041ce1
Run black formatter
Apr 30, 2021
e54f7fe
Fix mypy errors
Apr 30, 2021
059af6d
Add mypy into test install
Apr 30, 2021
dcd1fb6
Remove redundant mypy install
Apr 30, 2021
eb9649b
Type hint publish file
jorwoods Apr 30, 2021
4441377
Type hint datasource publish
jorwoods Apr 30, 2021
0ce6636
Split Union return type into overloads
jorwoods Oct 18, 2021
83327c4
Type hinting on datasource item
jorwoods Oct 19, 2021
4d88b6c
Merge remote-tracking branch 'upstream/development' into jorwoods/mypy
jorwoods Oct 19, 2021
eade29d
Fix type checking errors
jorwoods Oct 19, 2021
48517c6
Merge remote-tracking branch 'upstream/development' into jorwoods/mypy
jorwoods Oct 19, 2021
d70b27b
Make code compatible with 3.6 and 3.7
jorwoods Oct 19, 2021
f1fb183
Fix mypy errors
jorwoods Oct 19, 2021
742b1c9
Enable mypy
jorwoods Oct 19, 2021
bc7d63e
Ignore mypy import errors
jorwoods Oct 19, 2021
0d4dc2c
Remove pre-commit
jorwoods Oct 19, 2021
a6ab0b5
Formatting
jorwoods Oct 19, 2021
1f01be2
Fix black formatting oddities
jorwoods Oct 19, 2021
511f676
Add context to TypeErrors
jorwoods Oct 19, 2021
077c273
Add workbook test for TypeError
jorwoods Oct 19, 2021
4eeab5d
Type hint update_hyper_data
jorwoods Oct 19, 2021
27b4c61
Formatting
jorwoods Oct 19, 2021
1e41a91
Pin mypy version
jorwoods Oct 19, 2021
814430b
Better TypeError message
jorwoods Oct 19, 2021
bfccfb4
Use os.PathLike abstract class in isinstance
jorwoods Oct 19, 2021
a5997ec
Add test for publishing a Path object
jorwoods Oct 19, 2021
ee73215
Add str into PathOrFile
jorwoods Oct 19, 2021
56e8a93
Add type hints to tests
jorwoods Oct 19, 2021
9853fa1
Add pull request trigger to github actions
jorwoods Oct 20, 2021
de7037e
Add str to update_hyper_data type hints
jorwoods Oct 20, 2021
13da99d
Remove unused imports
jorwoods Oct 20, 2021
477e679
Fix actions type hint
jorwoods Oct 20, 2021
ab49a56
Change actions type to Sequence[Mapping]
jorwoods Oct 21, 2021
a0f4dd2
Fix tag type hints
jorwoods Oct 21, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: Python tests

on: [push]
on: [push, pull_request]

jobs:
build:
Expand All @@ -24,13 +24,11 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install -e .[test]
pip install mypy

- name: Test with pytest
run: |
pytest test

- name: Run Mypy but allow failures
- name: Run Mypy tests
run: |
mypy --show-error-codes --disable-error-code misc tableauserverclient
continue-on-error: true
mypy --show-error-codes --disable-error-code misc --disable-error-code import tableauserverclient test
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ smoke=pytest
[tool:pytest]
testpaths = test smoke
addopts = --junitxml=./test.junit.xml

[mypy]
ignore_missing_imports = True
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# This makes work easier for offline installs or low bandwidth machines
needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv)
pytest_runner = ['pytest-runner'] if needs_pytest else []
test_requirements = ['mock', 'pycodestyle', 'pytest', 'requests-mock>=1.0,<2.0']
test_requirements = ['mock', 'pycodestyle', 'pytest', 'requests-mock>=1.0,<2.0', 'mypy==0.910']

setup(
name='tableauserverclient',
Expand Down
2 changes: 1 addition & 1 deletion tableauserverclient/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class NotThisMethod(Exception):
"""Exception raised if a method is not valid for the current scenario."""


LONG_VERSION_PY = {}
LONG_VERSION_PY = {} # type: ignore
HANDLERS = {}


Expand Down
12 changes: 6 additions & 6 deletions tableauserverclient/exponential_backoff.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import time

# Polling for server-side events (such as job completion) uses exponential backoff for the sleep intervals between polls
ASYNC_POLL_MIN_INTERVAL=0.5
ASYNC_POLL_MAX_INTERVAL=30
ASYNC_POLL_BACKOFF_FACTOR=1.4
ASYNC_POLL_MIN_INTERVAL = 0.5
ASYNC_POLL_MAX_INTERVAL = 30
ASYNC_POLL_BACKOFF_FACTOR = 1.4


class ExponentialBackoffTimer():
class ExponentialBackoffTimer:
def __init__(self, *, timeout=None):
self.start_time = time.time()
self.timeout = timeout
Expand All @@ -15,7 +15,7 @@ def __init__(self, *, timeout=None):
def sleep(self):
max_sleep_time = ASYNC_POLL_MAX_INTERVAL
if self.timeout is not None:
elapsed = (time.time() - self.start_time)
elapsed = time.time() - self.start_time
if elapsed >= self.timeout:
raise TimeoutError(f"Timeout after {elapsed} seconds waiting for asynchronous event")
remaining_time = self.timeout - elapsed
Expand All @@ -27,4 +27,4 @@ def sleep(self):
max_sleep_time = max(max_sleep_time, ASYNC_POLL_MIN_INTERVAL)

time.sleep(min(self.current_sleep_interval, max_sleep_time))
self.current_sleep_interval *= ASYNC_POLL_BACKOFF_FACTOR
self.current_sleep_interval *= ASYNC_POLL_BACKOFF_FACTOR
10 changes: 5 additions & 5 deletions tableauserverclient/models/database_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ def dqws(self):
def content_permissions(self):
return self._content_permissions

@content_permissions.setter
@property_is_enum(ContentPermissions)
def content_permissions(self, value):
self._content_permissions = value

@property
def permissions(self):
if self._permissions is None:
Expand All @@ -67,11 +72,6 @@ def default_table_permissions(self):
raise UnpopulatedPropertyError(error)
return self._default_table_permissions()

@content_permissions.setter
@property_is_enum(ContentPermissions)
def content_permissions(self, value):
self._content_permissions = value

@property
def id(self):
return self._id
Expand Down
91 changes: 50 additions & 41 deletions tableauserverclient/models/datasource_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,86 +9,95 @@
from ..datetime_helpers import parse_datetime
import copy

from typing import Dict, List, Optional, Set, Tuple, TYPE_CHECKING, Union

if TYPE_CHECKING:
from .permissions_item import PermissionsRule
from .connection_item import ConnectionItem
import datetime


class DatasourceItem(object):
class AskDataEnablement:
Enabled = "Enabled"
Disabled = "Disabled"
SiteDefault = "SiteDefault"

def __init__(self, project_id, name=None):
def __init__(self, project_id: str, name: str = None) -> None:
self._ask_data_enablement = None
self._certified = None
self._certification_note = None
self._connections = None
self._content_url = None
self._content_url: Optional[str] = None
self._created_at = None
self._datasource_type = None
self._description = None
self._encrypt_extracts = None
self._has_extracts = None
self._id = None
self._initial_tags = set()
self._project_name = None
self._id: Optional[str] = None
self._initial_tags: Set = set()
self._project_name: Optional[str] = None
self._updated_at = None
self._use_remote_query_agent = None
self._webpage_url = None
self.description = None
self.name = name
self.owner_id = None
self.owner_id: Optional[str] = None
self.project_id = project_id
self.tags = set()
self.tags: Set[str] = set()

self._permissions = None
self._data_quality_warnings = None

return None

@property
def ask_data_enablement(self):
def ask_data_enablement(self) -> Optional["DatasourceItem.AskDataEnablement"]:
return self._ask_data_enablement

@ask_data_enablement.setter
@property_is_enum(AskDataEnablement)
def ask_data_enablement(self, value):
def ask_data_enablement(self, value: Optional["DatasourceItem.AskDataEnablement"]):
self._ask_data_enablement = value

@property
def connections(self):
def connections(self) -> Optional[List["ConnectionItem"]]:
if self._connections is None:
error = "Datasource item must be populated with connections first."
raise UnpopulatedPropertyError(error)
return self._connections()

@property
def permissions(self):
def permissions(self) -> Optional[List["PermissionsRule"]]:
if self._permissions is None:
error = "Project item must be populated with permissions first."
raise UnpopulatedPropertyError(error)
return self._permissions()

@property
def content_url(self):
def content_url(self) -> Optional[str]:
return self._content_url

@property
def created_at(self):
def created_at(self) -> Optional["datetime.datetime"]:
return self._created_at

@property
def certified(self):
def certified(self) -> Optional[bool]:
return self._certified

@certified.setter
@property_not_nullable
@property_is_boolean
def certified(self, value):
def certified(self, value: Optional[bool]):
self._certified = value

@property
def certification_note(self):
def certification_note(self) -> Optional[str]:
return self._certification_note

@certification_note.setter
def certification_note(self, value):
def certification_note(self, value: Optional[str]):
self._certification_note = value

@property
Expand All @@ -97,7 +106,7 @@ def encrypt_extracts(self):

@encrypt_extracts.setter
@property_is_boolean
def encrypt_extracts(self, value):
def encrypt_extracts(self, value: Optional[bool]):
self._encrypt_extracts = value

@property
Expand All @@ -108,53 +117,53 @@ def dqws(self):
return self._data_quality_warnings()

@property
def has_extracts(self):
def has_extracts(self) -> Optional[bool]:
return self._has_extracts

@property
def id(self):
def id(self) -> Optional[str]:
return self._id

@property
def project_id(self):
def project_id(self) -> str:
return self._project_id

@project_id.setter
@property_not_nullable
def project_id(self, value):
def project_id(self, value: str):
self._project_id = value

@property
def project_name(self):
def project_name(self) -> Optional[str]:
return self._project_name

@property
def datasource_type(self):
def datasource_type(self) -> Optional[str]:
return self._datasource_type

@property
def description(self):
def description(self) -> Optional[str]:
return self._description

@description.setter
def description(self, value):
def description(self, value: str):
self._description = value

@property
def updated_at(self):
def updated_at(self) -> Optional["datetime.datetime"]:
return self._updated_at

@property
def use_remote_query_agent(self):
def use_remote_query_agent(self) -> Optional[bool]:
return self._use_remote_query_agent

@use_remote_query_agent.setter
@property_is_boolean
def use_remote_query_agent(self, value):
def use_remote_query_agent(self, value: bool):
self._use_remote_query_agent = value

@property
def webpage_url(self):
def webpage_url(self) -> Optional[str]:
return self._webpage_url

def _set_connections(self, connections):
Expand Down Expand Up @@ -271,7 +280,7 @@ def _set_values(
self._webpage_url = webpage_url

@classmethod
def from_response(cls, resp, ns):
def from_response(cls, resp: str, ns: Dict) -> List["DatasourceItem"]:
all_datasource_items = list()
parsed_response = ET.fromstring(resp)
all_datasource_xml = parsed_response.findall(".//t:datasource", namespaces=ns)
Expand Down Expand Up @@ -322,16 +331,16 @@ def from_response(cls, resp, ns):
return all_datasource_items

@staticmethod
def _parse_element(datasource_xml, ns):
id_ = datasource_xml.get('id', None)
name = datasource_xml.get('name', None)
datasource_type = datasource_xml.get('type', None)
description = datasource_xml.get('description', None)
content_url = datasource_xml.get('contentUrl', None)
created_at = parse_datetime(datasource_xml.get('createdAt', None))
updated_at = parse_datetime(datasource_xml.get('updatedAt', None))
certification_note = datasource_xml.get('certificationNote', None)
certified = str(datasource_xml.get('isCertified', None)).lower() == 'true'
def _parse_element(datasource_xml: ET.Element, ns: Dict) -> Tuple:
id_ = datasource_xml.get("id", None)
name = datasource_xml.get("name", None)
datasource_type = datasource_xml.get("type", None)
description = datasource_xml.get("description", None)
content_url = datasource_xml.get("contentUrl", None)
created_at = parse_datetime(datasource_xml.get("createdAt", None))
updated_at = parse_datetime(datasource_xml.get("updatedAt", None))
certification_note = datasource_xml.get("certificationNote", None)
certified = str(datasource_xml.get("isCertified", None)).lower() == "true"
certification_note = datasource_xml.get("certificationNote", None)
certified = str(datasource_xml.get("isCertified", None)).lower() == "true"
content_url = datasource_xml.get("contentUrl", None)
Expand Down
8 changes: 0 additions & 8 deletions tableauserverclient/models/dqw_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,6 @@ def severe(self):
def severe(self, value):
self._severe = value

@property
def active(self):
return self._active

@active.setter
def active(self, value):
self._active = value

@property
def created_at(self):
return self._created_at
Expand Down
2 changes: 1 addition & 1 deletion tableauserverclient/models/job_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ class FinishCode:
Status codes as documented on
https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_jobs_tasks_and_schedules.htm#query_job
"""

Success = 0
Failed = 1
Cancelled = 2


def __init__(
self,
id_,
Expand Down
10 changes: 5 additions & 5 deletions tableauserverclient/models/project_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ def __init__(self, name, description=None, content_permissions=None, parent_id=N
def content_permissions(self):
return self._content_permissions

@content_permissions.setter
@property_is_enum(ContentPermissions)
def content_permissions(self, value):
self._content_permissions = value

@property
def permissions(self):
if self._permissions is None:
Expand Down Expand Up @@ -57,11 +62,6 @@ def default_flow_permissions(self):
raise UnpopulatedPropertyError(error)
return self._default_flow_permissions()

@content_permissions.setter
@property_is_enum(ContentPermissions)
def content_permissions(self, value):
self._content_permissions = value

@property
def id(self):
return self._id
Expand Down
Loading