Skip to content

Proposed: Remove pre-8.3 XML namespace fallback - #1863

Open
jacalata wants to merge 5 commits into
developmentfrom
jac/1046-remove-old-namespace
Open

jacalata wants to merge 5 commits into
developmentfrom
jac/1046-remove-old-namespace

Conversation

@jacalata

@jacalata jacalata commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Removes the runtime XML namespace detection that let TSC accept responses from Tableau Server versions predating 8.3 (2013, http://tableausoftware.com/api). The library's minimum_supported_server_version has been 2.3 for years (Tableau Server 10.0, 2016), so the detection has been unreachable for a decade.

Fixes #1046. Also makes #1866 moot: its application/xml vs text/xml guard in Endpoint._make_request is deleted here along with the detect() call.

What changes

Removed:

  • tableauserverclient/namespace.py (the whole module) and the symbols it exported: Namespace, UnknownNamespaceError, OLD_NAMESPACE, NEW_NAMESPACE, NAMESPACE_RE.
  • Server._namespace and the per-response .detect(...) call in Endpoint._make_request and both sign-in paths in Auth.

Kept:

  • Public TSC.DEFAULT_NAMESPACE re-export, with the same string value. It is a literal in __init__.py because importing it from server.server is circular; test/test_namespace.py pins it to server.server.NAMESPACE.
  • Server.namespace property, which returns a fresh {"t": NAMESPACE} dict on each call, the same shape callers pass to ElementTree's namespaces= kwarg.

Compatibility

Breaking for callers who import directly from tableauserverclient.namespace (Namespace, UnknownNamespaceError, OLD_NAMESPACE, NEW_NAMESPACE, NAMESPACE_RE) rather than the documented TSC.DEFAULT_NAMESPACE. The module was internal in every sense except being top-level, so this is an outright removal with no deprecation shim. CHANGELOG.md now points to GitHub Releases, so the release notes should carry this line.

Why now

Discussion in #1039 (2022) already noted the fallback was safe to drop; the issue has been open since. Nothing in the diff is complicated; the code is simply dead.

Written with Claude Code

The `Namespace` class detected `http://tableausoftware.com/api` (the pre-8.3
namespace) at runtime on every XML response and let the parser silently switch
namespaces if it saw the old one. That behavior has been unreachable for years:
the library's `minimum_supported_server_version` is 2.3, which corresponds to
Tableau Server 10.0 -- shipped in 2016, three years after the namespace changed.
Every server TSC has ever admitted uses `http://tableau.com/api`.

Removes:
- `tableauserverclient/namespace.py` (the module) and everything it exported:
  `Namespace`, `UnknownNamespaceError`, `OLD_NAMESPACE`, `NEW_NAMESPACE`,
  `NAMESPACE_RE`.
- `Server._namespace` instance and the per-response `.detect(...)` call in
  `Endpoint._make_request` and both sign-in paths in `Auth`.

Keeps:
- The public `TSC.DEFAULT_NAMESPACE` re-export -- now sourced from
  `tableauserverclient.server.server.NAMESPACE`, which is the canonical
  constant. Same string value.
- `Server.namespace` property -- still returns the `{"t": NAMESPACE}` dict
  callers pass to ElementTree's `namespaces=` kwarg. Same shape.

Callers who imported directly from `tableauserverclient.namespace` (rather
than `TSC.DEFAULT_NAMESPACE`) will break; the CHANGELOG entry calls this out.

Fixes #1046.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 16, 2026 •

Copy link
Copy Markdown

Coverage

Coverage Report
FileStmtsMissCoverMissing
tableauserverclient
   __init__.py50100% 
   config.py150100% 
   datetime_helpers.py2511 96%
   exponential_backoff.py200100% 
   filesys_helpers.py310100% 
tableauserverclient/bin
   __init__.py20100% 
   _version.py358212212 41%
tableauserverclient/helpers
   __init__.py10100% 
   logging.py20100% 
   strings.py3111 97%
tableauserverclient/models
   __init__.py470100% 
   auth_federation_item.py8799 90%
   collection_item.py4177 83%
   column_item.py553232 42%
   connection_credentials.py351111 69%
   connection_item.py1131313 88%
   custom_view_item.py1442121 85%
   data_acceleration_report_item.py5411 98%
   data_alert_item.py15844 97%
   data_freshness_policy_item.py1551515 90%
   database_item.py2073636 83%
   datasource_item.py3001212 96%
   dqw_item.py10455 95%
   exceptions.py40100% 
   extensions_item.py13244 97%
   extract_item.py4444 91%
   favorites_item.py6988 88%
   fileupload_item.py190100% 
   flow_item.py1491010 93%
   flow_run_item.py710100% 
   group_item.py8966 93%
   groupset_item.py4977 86%
   interval_item.py1823232 82%
   job_item.py1921010 95%
   linked_tasks_item.py7911 99%
   location_item.py2922 93%
   metric_item.py1291313 90%
   oidc_item.py6333 95%
   pagination_item.py3411 97%
   permissions_item.py1111212 89%
   project_item.py2073131 85%
   property_decorators.py1001818 82%
   reference_item.py2622 92%
   revision_item.py5911 98%
   schedule_item.py20966 97%
   server_info_item.py3777 81%
   site_item.py6361313 98%
   subscription_item.py10122 98%
   table_item.py1191818 85%
   tableau_auth.py612525 59%
   tableau_types.py2711 96%
   tag_item.py150100% 
   target.py60100% 
   task_item.py5622 96%
   user_item.py3381717 95%
   view_item.py2201616 93%
   virtual_connection_item.py6488 88%
   webhook_item.py6911 99%
   workbook_item.py3621616 96%
tableauserverclient/server
   __init__.py90100% 
   exceptions.py40100% 
   filter.py2911 97%
   pager.py3311 97%
   query.py1431515 90%
   request_factory.py1366194194 86%
   request_options.py38655 99%
   server.py2502525 90%
   sort.py60100% 
tableauserverclient/server/endpoint
   __init__.py360100% 
   auth_endpoint.py721010 86%
   auth_federation_endpoint.py800100% 
   custom_views_endpoint.py1521212 92%
   data_acceleration_report_endpoint.py210100% 
   data_alert_endpoint.py942323 76%
   databases_endpoint.py1113030 73%
   datasources_endpoint.py3233333 90%
   default_permissions_endpoint.py4433 93%
   dqw_endpoint.py451616 64%
   endpoint.py2582424 91%
   exceptions.py7966 92%
   extensions_endpoint.py310100% 
   favorites_endpoint.py942222 77%
   fileuploads_endpoint.py510100% 
   flow_runs_endpoint.py6299 85%
   flow_task_endpoint.py2122 90%
   flows_endpoint.py1984747 76%
   groups_endpoint.py12699 93%
   groupsets_endpoint.py7277 90%
   jobs_endpoint.py6899 87%
   linked_tasks_endpoint.py370100% 
   metadata_endpoint.py881414 84%
   metrics_endpoint.py5566 89%
   oidc_endpoint.py4211 98%
   permissions_endpoint.py4433 93%
   projects_endpoint.py1782424 87%
   resource_tagger.py1273535 72%
   schedules_endpoint.py1191111 91%
   server_info_endpoint.py361010 72%
   sites_endpoint.py1302727 79%
   subscriptions_endpoint.py561414 75%
   tables_endpoint.py1103636 67%
   tasks_endpoint.py6366 90%
   users_endpoint.py17077 96%
   views_endpoint.py15099 94%
   virtual_connections_endpoint.py1131010 91%
   webhooks_endpoint.py5499 83%
   workbooks_endpoint.py3382222 93%
TOTAL12321142488% 

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR removes runtime XML namespace detection that allowed parsing legacy (pre-Tableau Server 8.3) REST API XML responses, standardizing the client on the modern http://tableau.com/api namespace in line with the library’s long-standing minimum supported server/API versions.

Changes:

  • Removed the tableauserverclient.namespace module and eliminated per-response namespace detection calls.
  • Centralized the namespace constant in server.server and updated TSC.DEFAULT_NAMESPACE to keep the public re-export working.
  • Updated server/endpoint code paths to always use the fixed namespace map.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tableauserverclient/server/server.py Removes per-instance namespace detector and introduces fixed namespace constants/map.
tableauserverclient/server/endpoint/endpoint.py Removes the XML-response namespace detection hook in _make_request.
tableauserverclient/server/endpoint/auth_endpoint.py Removes namespace detection during sign-in and site switching flows.
tableauserverclient/namespace.py Deletes the legacy namespace detection module and exported symbols.
tableauserverclient/init.py Updates DEFAULT_NAMESPACE re-export to come from the new namespace source.
CHANGELOG.md Documents the breaking removal of legacy namespace fallback and module exports.
Suppressed comments (2)

tableauserverclient/server/endpoint/auth_endpoint.py:159

  • With namespace auto-detection removed, a legacy-namespace (or otherwise unexpected) XML response can make find(...).get(...) raise AttributeError. Adding an explicit check and raising ServerResponseError produces a clearer, user-facing failure mode for unsupported namespaces/servers.
        parsed_response = fromstring(server_response.content)
        site_id = parsed_response.find(".//t:site", namespaces=self.parent_srv.namespace).get("id", None)
        site_url = parsed_response.find(".//t:site", namespaces=self.parent_srv.namespace).get("contentUrl", None)

tableauserverclient/server/endpoint/auth_endpoint.py:86

  • With namespace auto-detection removed, a legacy-namespace (or otherwise unexpected) XML response will make find(...).get(...) raise an AttributeError, which is hard to diagnose. It’s better to explicitly check that the expected elements are present and raise ServerResponseError with a message that points to a likely namespace / server-version mismatch.

This issue also appears on line 157 of the same file.

        parsed_response = fromstring(server_response.content)
        site_id = parsed_response.find(".//t:site", namespaces=self.parent_srv.namespace).get("id", None)
        site_url = parsed_response.find(".//t:site", namespaces=self.parent_srv.namespace).get("contentUrl", None)

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tableauserverclient/server/server.py Resolved
Comment thread tableauserverclient/__init__.py Resolved
…hared state

Copilot flagged that Server.namespace returned the module-level
_NAMESPACE_MAP directly, so a caller mutating the returned dict would
have polluted every other Server instance in the process. Old namespace-
handling code returned a per-instance dict, so no cross-instance
pollution was possible. Wrap in dict(...) to restore that behavior.
jacalata added a commit that referenced this pull request Aug 20, 2026
Two adjustments from a post-merge fresh-eyes pass:

- endpoint.py: the http -> https address-promotion match now compares
  hostnames case-insensitively (RFC 3986) and normalizes http://host
  vs http://host:80 so a same-host promotion doesn't silently drop.
  Split into two comparisons: current-vs-next is hostname-only
  (schemes differ so default port differs, comparing raw netloc
  would spuriously mismatch); old-address-vs-current is same-scheme
  and uses (hostname, effective port) so explicit-vs-implicit port
  compares equal. Expanded the auth-material comment to acknowledge
  that sign_in itself carries raw credentials in the POST body, not
  only the issued token on subsequent calls.

- test_redirect_handling.py: added a docstring on
  test_all_supported_redirect_codes_preserve_post_body naming the
  RFC 7231 6.4.4 deviation on 303 -- if a future refactor
  "helpfully" converts 303 to GET, the parametrized test fails
  with a clear intent statement.

Also considered a sign_in namespace-detect hedge for pre-8.3
Tableau responses (Copilot flagged this on #1848); dropped as
theoretical because TSC's minimum_supported_server_version = 2.3
(Tableau 10.0, 2016) is eight years past the namespace change,
and #1863 removes the whole subsystem anyway.
@jacalata jacalata changed the title Remove pre-8.3 XML namespace fallback Proposed: Remove pre-8.3 XML namespace fallback Aug 20, 2026
…e-old-namespace

# Conflicts:
#	tableauserverclient/server/endpoint/auth_endpoint.py
Cosmetic import-graph cleanup on top of #1863's move of `NAMESPACE`
from `namespace.py` into `server/server.py`. Before this change,
`tableauserverclient/__init__.py:2` did `from tableauserverclient.
server.server import NAMESPACE`, which forces `server.server` (and
its transitive endpoint tree) to load just to read a string
constant.

Copilot flagged the circular-import path this creates: `server.
server` -> `server.endpoint.endpoint` -> `from tableauserverclient
import ...` -> `__init__` -> `server.server`. Python resolves the
chain today because the reentrant lookups pull submodules whose
own load doesn't back-reference the partially-initialized parent,
but the shape is fragile and adding a new top-level import to
`__init__.py` before line 2 could easily break it.

Replaced the import with the string literal. Kept in sync with
the `NAMESPACE` constant in `server/server.py`. Also removes one
of the two entry points into `server.server` at package load,
though everything else in `__init__.py` still transitively pulls
the endpoint tree via the models block below, so no real startup
savings -- this is defensive, not a perf fix.
CHANGELOG.md: development now points to the GitHub Releases page, so the
changelog entry for this change is dropped (the breaking-change note moves
to the PR description).
tableauserverclient/namespace.py: deletion kept; development only added
docstrings to the removed module.

Add test/test_namespace.py pinning TSC.DEFAULT_NAMESPACE to server.NAMESPACE
and checking Server.namespace returns an independent copy.

Written with Claude Code
@jacalata
jacalata marked this pull request as ready for review November 10, 2026 23:02
Copilot AI lite review requested due to automatic review settings November 10, 2026 23:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are identified, and the change consistently removes obsolete namespace detection while preserving the documented public interfaces and defensive mapping behavior.

No open issues 🎉

2 resolved since last review

Review effort: Lite

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop supporting old namespaces

2 participants