Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
Comment thread
jacalata marked this conversation as resolved.

# 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'."""
Comment thread
jacalata marked this conversation as resolved.
Outdated
# 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