Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions tableauserverclient/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,24 @@


class Config:
"""Runtime configuration for TSC, controllable via environment variables."""

# The maximum size of a file that can be published in a single request is 64MB
@property
def FILESIZE_LIMIT_MB(self):
"""Maximum single-request publish size in MB (env: TSC_FILESIZE_LIMIT_MB, capped at 64)."""
return min(int(os.getenv("TSC_FILESIZE_LIMIT_MB", 64)), 64)

# For when a datasource is over 64MB, break it into 5MB(standard chunk size) chunks
@property
def CHUNK_SIZE_MB(self):
"""Chunk size in MB for multipart publish requests (env: TSC_CHUNK_SIZE_MB)."""
return int(os.getenv("TSC_CHUNK_SIZE_MB", 5 * 10)) # 5MB felt too slow, upped it to 50

# Default page size
@property
def PAGE_SIZE(self):
"""Default page size for paginated API requests (env: TSC_PAGE_SIZE)."""
return int(os.getenv("TSC_PAGE_SIZE", 100))


Expand Down
5 changes: 4 additions & 1 deletion tableauserverclient/datetime_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@


def timestamp():
"""Return the current local time as an HH:MM:SS string."""
return datetime.datetime.now().strftime("%H:%M:%S")


# This class is a concrete implementation of the abstract base class tzinfo
# docs: https://docs.python.org/2.3/lib/datetime-tzinfo.html
class UTC(datetime.tzinfo):
"""UTC"""
"""UTC timezone implementation for use with datetime objects."""

def utcoffset(self, dt):
return ZERO
Expand All @@ -28,6 +29,7 @@ def dst(self, dt):


def parse_datetime(date):
"""Parse a Tableau API datetime string into a UTC-aware datetime, or None if absent or unparseable."""
if date is None:
return None

Expand All @@ -38,6 +40,7 @@ def parse_datetime(date):


def format_datetime(date):
"""Format a datetime as a Tableau API datetime string, or None if absent."""
if date is None:
return None

Expand Down
3 changes: 3 additions & 0 deletions tableauserverclient/exponential_backoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@


class ExponentialBackoffTimer:
"""Timer for polling server-side events with exponential backoff between attempts."""

def __init__(self, *, timeout=None):
self.start_time = time.time()
self.timeout = timeout
self.current_sleep_interval = ASYNC_POLL_MIN_INTERVAL

def sleep(self):
"""Sleep for the next backoff interval, raising TimeoutError if the timeout deadline has passed."""
max_sleep_time = ASYNC_POLL_MAX_INTERVAL
if self.timeout is not None:
elapsed = time.time() - self.start_time
Expand Down
4 changes: 4 additions & 0 deletions tableauserverclient/filesys_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@


def to_filename(string_to_sanitize):
"""Strip characters from a string that are not safe for use in a filename."""
sanitized = (c for c in string_to_sanitize if c.isalnum() or c in ALLOWED_SPECIAL)
return "".join(sanitized)


def make_download_path(filepath, filename):
"""Resolve a download destination path from an optional target filepath and the server-provided filename."""
download_path = None

if filepath is None:
Expand All @@ -24,6 +26,7 @@ def make_download_path(filepath, filename):


def get_file_object_size(file):
"""Return the size in bytes of an open binary file object."""
# Returns the size of a file object
file.seek(0, os.SEEK_END)
file_size = file.tell()
Expand All @@ -32,6 +35,7 @@ def get_file_object_size(file):


def get_file_type(file):
"""Detect the type of an open binary file by inspecting its magic bytes, returning one of 'zip', 'tde', 'xml', or 'hyper'."""
# Tableau workbooks (twb) and data sources (tds) are both stored as xml files.
# Packaged workbooks (twbx) and data sources (tdsx) are zip files
# containing original files accompanied with supporting local files.
Expand Down
5 changes: 4 additions & 1 deletion tableauserverclient/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@


class UnknownNamespaceError(Exception):
pass
"""Raised when an XML response contains an unrecognized Tableau API namespace."""


class Namespace:
"""Detects and stores the Tableau REST API XML namespace from server responses."""

def __init__(self):
self._namespace = {"t": NEW_NAMESPACE}
self._detected = False
Expand All @@ -20,6 +22,7 @@ def __call__(self):
return self._namespace

def detect(self, xml):
"""Detect the XML namespace from raw response bytes, updating the stored namespace on first call."""
if self._detected:
return

Expand Down
Loading