From d9c4d6d3a3654488b1e72fbaeb4339656a54af48 Mon Sep 17 00:00:00 2001 From: Zayan Khan Date: Sun, 26 Jul 2026 20:03:23 -0400 Subject: [PATCH 1/2] Remove distutils dependency for Python 3.12+ compatibility distutils was removed from the standard library in Python 3.12, so 'from distutils.version import LooseVersion' fails on a clean install (it only works today when setuptools happens to be present and shims distutils). Tableau file versions are simple dotted numerics, so parse them into comparable tuples locally instead of depending on LooseVersion; the unsupported-version exception now carries the raw version string. Co-Authored-By: Claude Fable 5 --- tableaudocumentapi/xfile.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tableaudocumentapi/xfile.py b/tableaudocumentapi/xfile.py index 8d2bb9f..ebbbcd3 100644 --- a/tableaudocumentapi/xfile.py +++ b/tableaudocumentapi/xfile.py @@ -5,9 +5,20 @@ import zipfile from lxml import etree as ET -from distutils.version import LooseVersion as Version -MIN_SUPPORTED_VERSION = Version("9.0") +def _parse_version(version_string): + """Parse a dotted Tableau file version (e.g. '9.0', '18.1') into a + comparable tuple, treating any non-numeric segment as 0.""" + parts = [] + for part in version_string.split('.'): + try: + parts.append(int(part)) + except ValueError: + parts.append(0) + return tuple(parts) + + +MIN_SUPPORTED_VERSION = _parse_version("9.0") class TableauVersionNotSupportedException(Exception): @@ -31,9 +42,9 @@ def xml_open(filename, expected_root=None): # Is the file a supported version tree_root = tree.getroot() - file_version = Version(tree_root.attrib.get('version', '0.0')) + file_version = tree_root.attrib.get('version', '0.0') - if file_version < MIN_SUPPORTED_VERSION: + if _parse_version(file_version) < MIN_SUPPORTED_VERSION: raise TableauVersionNotSupportedException(file_version) # Does the root tag match the object type (workbook or data source) From 72ecf07c460903bb861f6179f5b113606987ab85 Mon Sep 17 00:00:00 2001 From: Zayan Khan Date: Sun, 26 Jul 2026 20:03:23 -0400 Subject: [PATCH 2/2] docs: fix API reference drift and supported-format claim - save_as takes new_filename (workbook.py), not new_file - Field.create_field_xml requires six positional arguments (field.py), not zero - the library handles TWB/TWBX/TDS/TDSX; nothing in the package reads TDE (find_file_in_zip only extracts twb/tds entries) Co-Authored-By: Claude Fable 5 --- README.md | 2 +- docs/docs/api-ref.md | 4 ++-- docs/index.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index beb0263..069a3cb 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This repo contains Python source and example files for the Tableau Document API. The Document API provides a useful but *unsupported* way to programmatically make updates to Tableau workbook and data source files. If you've been making changes to these file types by directly updating the XML--that is, by XML hacking--this SDK is for you :) Get help from other users on the [Tableau Community Forums](https://community.tableau.com/s/topic/0TO4T000000SF3sWAG/document-api). Features include: -- Support for TWB, TWBX, TDE and TDSX files starting roughly back to Tableau 9.x +- Support for TWB, TWBX, TDS and TDSX files starting roughly back to Tableau 9.x - Getting connection information from data sources and workbooks - Server Name - Username diff --git a/docs/docs/api-ref.md b/docs/docs/api-ref.md index 1dd932a..96abac4 100644 --- a/docs/docs/api-ref.md +++ b/docs/docs/api-ref.md @@ -28,7 +28,7 @@ The Workbook class represents a tableau workbook. It may be either a TWB or TWBX Saves any changes to the workbook to the existing file. `Workbook.save_as(self, new_filename):` -Saves any changes to the workbook to a new file specified by the `new_file` parameter. +Saves any changes to the workbook to a new file specified by the `new_filename` parameter. **Properties:** @@ -117,7 +117,7 @@ Represents a field in a datasource **Raises:** **Methods:** -`Field.create_field_xml()` Create field from scratch. +`Field.create_field_xml(caption, datatype, hidden, role, field_type, name)` Create field from scratch. `Field.add_alias(self, key, value)` Add an alias for a given display value. diff --git a/docs/index.md b/docs/index.md index fdf461b..cc30674 100644 --- a/docs/index.md +++ b/docs/index.md @@ -17,7 +17,7 @@ The Document API provides an *unsupported* way to programmatically make updates Features include: -- Support for TWB, TWBX, TDE and TDSX files starting roughly back to Tableau 9.x +- Support for TWB, TWBX, TDS and TDSX files starting roughly back to Tableau 9.x - Getting connection information from data sources and workbooks - Server Name - Username