From 692b54fc96c3085d8494ce91f6ce0d68241311ac Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 20 Aug 2026 18:51:10 -0700 Subject: [PATCH 1/2] feat: expose JobFailedException and JobCancelledException at the top level Both were previously reachable only via `tableauserverclient.server.endpoint.exceptions`, which is an internal module path. Callers waiting on a background job with `server.jobs.wait_for_job()` need to catch these to distinguish a failed job from a cancelled one (JobCancelledException is a subclass of JobFailedException, so the order in an except-chain matters), and reaching into a `server.endpoint` sub-package to do so is a bad pattern to teach. Re-export them alongside the other exception types already exposed at the top level (FailedSignInError, MissingRequiredFieldError, NotSignedInError, ServerResponseError). Existing internal imports in tests and endpoints remain unchanged; this is purely additive. Co-Authored-By: Claude Opus 4.7 (1M context) --- tableauserverclient/__init__.py | 4 ++++ tableauserverclient/server/__init__.py | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tableauserverclient/__init__.py b/tableauserverclient/__init__.py index 7241f23ca..a3264b0fd 100644 --- a/tableauserverclient/__init__.py +++ b/tableauserverclient/__init__.py @@ -67,6 +67,8 @@ RequestOptions, MissingRequiredFieldError, FailedSignInError, + JobCancelledException, + JobFailedException, NotSignedInError, ServerResponseError, Filter, @@ -105,6 +107,8 @@ "HourlyInterval", "ImageRequestOptions", "IntervalItem", + "JobCancelledException", + "JobFailedException", "JobItem", "JWTAuth", "LinkedTaskFlowRunItem", diff --git a/tableauserverclient/server/__init__.py b/tableauserverclient/server/__init__.py index 55288fdc9..b28821bc0 100644 --- a/tableauserverclient/server/__init__.py +++ b/tableauserverclient/server/__init__.py @@ -12,7 +12,12 @@ from tableauserverclient.server.sort import Sort from tableauserverclient.server.server import Server from tableauserverclient.server.pager import Pager -from tableauserverclient.server.endpoint.exceptions import FailedSignInError, NotSignedInError +from tableauserverclient.server.endpoint.exceptions import ( + FailedSignInError, + JobCancelledException, + JobFailedException, + NotSignedInError, +) from tableauserverclient.server.endpoint import ( Auth, @@ -60,6 +65,8 @@ "Server", "Pager", "FailedSignInError", + "JobCancelledException", + "JobFailedException", "NotSignedInError", "Auth", "CustomViews", From a3663eea627610b14e749aec85cd1eee24c88e64 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Wed, 9 Sep 2026 16:13:00 -0700 Subject: [PATCH 2/2] Also expose FlowRunFailedException and FlowRunCancelledException at top level Same shape and same private-path problem as the Job exceptions this PR is already fixing. flow_runs.wait_for_flow_run() raises these; users need to catch them without importing from server.endpoint.exceptions. Add regression tests for both pairs so a future refactor of __init__.py can't silently break the public surface. Co-Authored-By: Claude Opus 4.7 (1M context) --- tableauserverclient/__init__.py | 4 ++++ tableauserverclient/server/__init__.py | 4 ++++ test/test_import_surface.py | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 test/test_import_surface.py diff --git a/tableauserverclient/__init__.py b/tableauserverclient/__init__.py index a3264b0fd..cb197d47c 100644 --- a/tableauserverclient/__init__.py +++ b/tableauserverclient/__init__.py @@ -67,6 +67,8 @@ RequestOptions, MissingRequiredFieldError, FailedSignInError, + FlowRunCancelledException, + FlowRunFailedException, JobCancelledException, JobFailedException, NotSignedInError, @@ -100,6 +102,8 @@ "FileuploadItem", "Filter", "FlowItem", + "FlowRunCancelledException", + "FlowRunFailedException", "FlowRunItem", "get_versions", "GroupItem", diff --git a/tableauserverclient/server/__init__.py b/tableauserverclient/server/__init__.py index b28821bc0..44020419b 100644 --- a/tableauserverclient/server/__init__.py +++ b/tableauserverclient/server/__init__.py @@ -14,6 +14,8 @@ from tableauserverclient.server.pager import Pager from tableauserverclient.server.endpoint.exceptions import ( FailedSignInError, + FlowRunCancelledException, + FlowRunFailedException, JobCancelledException, JobFailedException, NotSignedInError, @@ -65,6 +67,8 @@ "Server", "Pager", "FailedSignInError", + "FlowRunCancelledException", + "FlowRunFailedException", "JobCancelledException", "JobFailedException", "NotSignedInError", diff --git a/test/test_import_surface.py b/test/test_import_surface.py new file mode 100644 index 000000000..d2069aa64 --- /dev/null +++ b/test/test_import_surface.py @@ -0,0 +1,16 @@ +import tableauserverclient as TSC +from tableauserverclient.server.endpoint import exceptions + + +def test_job_exceptions_at_top_level(): + assert TSC.JobFailedException is exceptions.JobFailedException + assert TSC.JobCancelledException is exceptions.JobCancelledException + assert "JobFailedException" in TSC.__all__ + assert "JobCancelledException" in TSC.__all__ + + +def test_flow_run_exceptions_at_top_level(): + assert TSC.FlowRunFailedException is exceptions.FlowRunFailedException + assert TSC.FlowRunCancelledException is exceptions.FlowRunCancelledException + assert "FlowRunFailedException" in TSC.__all__ + assert "FlowRunCancelledException" in TSC.__all__