From 6da78585f645d1c8a27bbd0f4402b79149a09a2f Mon Sep 17 00:00:00 2001 From: Jac Date: Sat, 11 Mar 2023 14:21:49 -0800 Subject: [PATCH 01/10] Jac/parse publish options (#236) * implement thumbnail options. (including 'not yet implemented' message for --thumbnail-group) * Implement overwrite/append/replace arguments (including not-yet-implemented message for --replace) * clarified help content for extract-encryption and some other not-implemented options * added tests for all valid publish command options * error log cleanup: better stacktrace, less repetition --- tabcmd/commands/datasources_and_workbooks/publish_command.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index 64189e9e..5aaf7db2 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -74,6 +74,7 @@ def run_command(args): new_workbook = TSC.WorkbookItem(project_id, name=args.name, show_tabs=args.tabbed) try: + print(creds) new_workbook = server.workbooks.publish( new_workbook, args.filename, From 9272e70bfdf39344dc92d488a4cdcba25b58f006 Mon Sep 17 00:00:00 2001 From: Jac Date: Thu, 27 Jul 2023 00:29:00 -0700 Subject: [PATCH 02/10] Remove duplicated tests in merge Also change a print statement to log statement --- tabcmd/commands/datasources_and_workbooks/publish_command.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index 5aaf7db2..99c4ea25 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -74,7 +74,8 @@ def run_command(args): new_workbook = TSC.WorkbookItem(project_id, name=args.name, show_tabs=args.tabbed) try: - print(creds) + if creds: + logger.debug("Workbook credentials object: " + creds) new_workbook = server.workbooks.publish( new_workbook, args.filename, From 1784ac5578dc1b6bd8ba630a882562416bf81798 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 27 Jul 2023 17:47:18 -0700 Subject: [PATCH 03/10] ignore errors from missing var files when in github --- tests/e2e/setup_e2e.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/setup_e2e.py b/tests/e2e/setup_e2e.py index 29213fe7..7253fd6e 100644 --- a/tests/e2e/setup_e2e.py +++ b/tests/e2e/setup_e2e.py @@ -4,7 +4,7 @@ try: from tests.e2e import credentials # type: ignore except ImportError: - credentials = None # type: ignore + credentials = {} # type: ignore our_program = "tabcmd.exe" launch_path = os.path.join("dist", "tabcmd") @@ -13,7 +13,7 @@ def login(extra="--language", value="en"): if not credentials: - return + return # when run on github # --server, --site, --username, --password args = [ "python", From 6caea34ae2d01ce39763885df57434722928fc3f Mon Sep 17 00:00:00 2001 From: Jac Date: Fri, 28 Jul 2023 16:28:12 -0700 Subject: [PATCH 04/10] fix log error on publish (#262) also add unit test --- .../publish_command.py | 2 +- tabcmd/commands/site/list_command.py | 1 - tests/commands/test_publish_command.py | 90 +++++++++++++++++++ tests/commands/test_run_commands.py | 37 +------- tests/commands/test_session.py | 2 + 5 files changed, 95 insertions(+), 37 deletions(-) create mode 100644 tests/commands/test_publish_command.py diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index 99c4ea25..cb50be17 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -75,7 +75,7 @@ def run_command(args): new_workbook = TSC.WorkbookItem(project_id, name=args.name, show_tabs=args.tabbed) try: if creds: - logger.debug("Workbook credentials object: " + creds) + logger.debug("Workbook credentials object: " + str(creds)) new_workbook = server.workbooks.publish( new_workbook, args.filename, diff --git a/tabcmd/commands/site/list_command.py b/tabcmd/commands/site/list_command.py index 76c67cbb..64e4fa48 100644 --- a/tabcmd/commands/site/list_command.py +++ b/tabcmd/commands/site/list_command.py @@ -18,7 +18,6 @@ class ListCommand(Server): "tabcmd_content_none": "No content found.", } - name: str = "list" description: str = "List content items of a specified type" diff --git a/tests/commands/test_publish_command.py b/tests/commands/test_publish_command.py new file mode 100644 index 00000000..9f91a02e --- /dev/null +++ b/tests/commands/test_publish_command.py @@ -0,0 +1,90 @@ +import argparse +import unittest +from unittest.mock import * +import tableauserverclient as TSC + +from tabcmd.commands.auth import login_command +from tabcmd.commands.datasources_and_workbooks import delete_command, export_command, get_url_command, publish_command + + +from typing import List, NamedTuple, TextIO, Union +import io + +mock_args = argparse.Namespace() + +fake_item = MagicMock() +fake_item.name = "fake-name" +fake_item.id = "fake-id" +fake_item.pdf = b"/pdf-representation-of-view" +fake_item.extract_encryption_mode = "Disabled" + +fake_job = MagicMock() +fake_job.id = "fake-job-id" + +creator = MagicMock() +getter = MagicMock() +getter.get = MagicMock("get", return_value=([fake_item], 1)) +getter.publish = MagicMock("publish", return_value=fake_item) + + +@patch("tableauserverclient.Server") +@patch("tabcmd.commands.auth.session.Session.create_session") +class RunCommandsTest(unittest.TestCase): + @staticmethod + def _set_up_session(mock_session, mock_server): + mock_session.return_value = mock_server + assert mock_session is not None + mock_session.assert_not_called() + global mock_args + mock_args = argparse.Namespace(logging_level="DEBUG") + # set values for things that should always have a default + # should refactor so this can be automated + mock_args.continue_if_exists = False + mock_args.project_name = None + mock_args.parent_project_path = None + mock_args.parent_path = None + mock_args.timeout = None + mock_args.username = None + + def test_publish(self, mock_session, mock_server): + RunCommandsTest._set_up_session(mock_session, mock_server) + mock_args.overwrite = False + mock_args.filename = "existing_file.twbx" + mock_args.project_name = "project-name" + mock_args.parent_project_path = "projects" + mock_args.name = "" + mock_args.tabbed = True + mock_args.db_username = None + mock_args.oauth_username = None + mock_args.append = False + mock_args.replace = False + mock_args.thumbnail_username = None + mock_args.thumbnail_group = None + mock_server.projects = getter + publish_command.PublishCommand.run_command(mock_args) + mock_session.assert_called() + + def test_publish_with_creds(self, mock_session, mock_server): + RunCommandsTest._set_up_session(mock_session, mock_server) + mock_args.overwrite = False + mock_args.append = True + mock_args.replace = False + + mock_args.filename = "existing_file.twbx" + mock_args.project_name = "project-name" + mock_args.parent_project_path = "projects" + mock_args.name = "" + mock_args.tabbed = True + + mock_args.db_username = "username" + mock_args.db_password = "oauth_u" + mock_args.save_db_password = True + mock_args.oauth_username = None + mock_args.embed = False + + mock_args.thumbnail_username = None + mock_args.thumbnail_group = None + + mock_server.projects = getter + publish_command.PublishCommand.run_command(mock_args) + mock_session.assert_called() diff --git a/tests/commands/test_run_commands.py b/tests/commands/test_run_commands.py index dbb9bf55..2efd4a75 100644 --- a/tests/commands/test_run_commands.py +++ b/tests/commands/test_run_commands.py @@ -4,13 +4,7 @@ import tableauserverclient as TSC from tabcmd.commands.auth import login_command, logout_command -from tabcmd.commands.datasources_and_workbooks import ( - delete_command, - export_command, - get_url_command, - publish_command, - runschedule_command, -) +from tabcmd.commands.datasources_and_workbooks import delete_command, export_command, get_url_command, publish_command from tabcmd.commands.extracts import ( create_extracts_command, delete_extracts_command, @@ -35,7 +29,7 @@ remove_users_command, delete_site_users_command, ) -from typing import List, NamedTuple, TextIO, Union +from typing import NamedTuple, TextIO, Union import io mock_args = argparse.Namespace() @@ -139,33 +133,6 @@ def test_get_workbook(self, mock_session, mock_server): get_url_command.GetUrl.run_command(mock_args) mock_session.assert_called() - def test_publish(self, mock_session, mock_server): - RunCommandsTest._set_up_session(mock_session, mock_server) - mock_args.overwrite = False - mock_args.filename = "existing_file.twbx" - mock_args.project_name = "project-name" - mock_args.parent_project_path = "projects" - mock_args.name = "" - mock_args.tabbed = True - mock_args.db_username = None - mock_args.oauth_username = None - mock_args.append = False - mock_args.replace = False - mock_args.thumbnail_username = None - mock_args.thumbnail_group = None - mock_server.projects = getter - publish_command.PublishCommand.run_command(mock_args) - mock_session.assert_called() - - @unittest.skip("target code not implemented yet") - def test_runschedule(self, mock_session, mock_server): - RunCommandsTest._set_up_session(mock_session, mock_server) - mock_server.schedules = getter - mock_args.schedule = "myschedule" - with self.assertRaises(SystemExit): - runschedule_command.RunSchedule.run_command(mock_args) - # mock_session.assert_called() - # extracts def test_create_extract(self, mock_session, mock_server): RunCommandsTest._set_up_session(mock_session, mock_server) diff --git a/tests/commands/test_session.py b/tests/commands/test_session.py index 44b20a8a..cb60260d 100644 --- a/tests/commands/test_session.py +++ b/tests/commands/test_session.py @@ -74,6 +74,7 @@ def _set_mocks_for_json_file_exists(mock_path, mock_json_lib, does_it_exist=True mock_json_lib.load.return_value = None return path + def _set_mock_file_content(mock_load, expected_content): mock_load.return_value = expected_content return mock_load @@ -120,6 +121,7 @@ def test_json_invalid(self, mock_open, mock_path, mock_json): test_session = Session() assert test_session.username is None + @mock.patch("getpass.getpass") class BuildCredentialsTests(unittest.TestCase): @classmethod From 305718e556959b75551bea5c9f28e3aa4f6242c0 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 17 Aug 2023 02:04:15 -0700 Subject: [PATCH 05/10] enable type checking everywhere (cherry picked from commit ef93fff1280288a41e9eaf4764faefc30befd9d1) --- pyproject.toml | 1 + tabcmd/commands/auth/login_command.py | 6 ++--- tabcmd/commands/auth/logout_command.py | 6 ++--- tabcmd/commands/auth/session.py | 22 +++++++++++++------ .../datasources_and_workbooks_command.py | 3 --- .../delete_command.py | 8 +++---- .../export_command.py | 8 +++---- .../get_url_command.py | 14 ++++++------ .../publish_command.py | 6 ++--- .../runschedule_command.py | 6 ++--- .../extracts/create_extracts_command.py | 13 ++++++----- .../extracts/decrypt_extracts_command.py | 8 +++---- .../extracts/delete_extracts_command.py | 13 ++++++----- .../extracts/encrypt_extracts_command.py | 8 +++---- tabcmd/commands/extracts/extracts.py | 7 +++--- .../extracts/reencrypt_extracts_command.py | 8 +++---- .../extracts/refresh_extracts_command.py | 13 ++++++----- tabcmd/commands/group/create_group_command.py | 6 ++--- tabcmd/commands/group/delete_group_command.py | 6 ++--- .../project/create_project_command.py | 12 +++++----- .../project/delete_project_command.py | 8 +++---- .../project/publish_samples_command.py | 6 ++--- tabcmd/commands/server.py | 6 +++-- tabcmd/commands/site/create_site_command.py | 8 +++---- tabcmd/commands/site/delete_site_command.py | 6 ++--- tabcmd/commands/site/edit_site_command.py | 8 +++---- tabcmd/commands/site/list_command.py | 6 ++--- tabcmd/commands/site/list_sites_command.py | 8 +++---- tabcmd/commands/user/add_users_command.py | 6 ++--- tabcmd/commands/user/create_site_users.py | 6 ++--- tabcmd/commands/user/create_users_command.py | 6 ++--- .../user/delete_site_users_command.py | 6 ++--- tabcmd/commands/user/remove_users_command.py | 6 ++--- tabcmd/execution/logger_config.py | 4 ++-- tabcmd/execution/parent_parser.py | 2 +- tabcmd/tabcmd.py | 15 +++++++------ tests/commands/test_session.py | 16 +++++++++----- tests/e2e/language_tests.py | 6 +++++ tests/e2e/tests_integration.py | 6 +++-- tests/parsers/common_setup.py | 10 +++++++-- tests/parsers/test_login_parser.py | 2 +- tests/parsers/test_logout_parser.py | 2 +- tests/parsers/test_parser_add_user.py | 4 ++-- tests/parsers/test_parser_create_extracts.py | 2 +- tests/parsers/test_parser_create_group.py | 2 +- tests/parsers/test_parser_create_project.py | 2 +- tests/parsers/test_parser_create_site.py | 2 +- .../parsers/test_parser_create_site_users.py | 2 +- tests/parsers/test_parser_create_user.py | 2 +- tests/parsers/test_parser_decrypt_extracts.py | 2 +- tests/parsers/test_parser_delete.py | 2 +- tests/parsers/test_parser_delete_extracts.py | 2 +- tests/parsers/test_parser_delete_group.py | 2 +- tests/parsers/test_parser_delete_project.py | 2 +- tests/parsers/test_parser_delete_site.py | 2 +- tests/parsers/test_parser_delete_site_user.py | 2 +- tests/parsers/test_parser_edit_site.py | 2 +- tests/parsers/test_parser_encrypt_extracts.py | 2 +- tests/parsers/test_parser_export.py | 2 +- tests/parsers/test_parser_get_url.py | 4 ++-- tests/parsers/test_parser_list_sites.py | 2 +- tests/parsers/test_parser_publish.py | 2 +- tests/parsers/test_parser_publish_samples.py | 2 +- .../parsers/test_parser_reencrypt_extracts.py | 2 +- tests/parsers/test_parser_refresh_extracts.py | 2 +- tests/parsers/test_parser_remove_user.py | 2 +- tests/parsers/test_parser_runschedule.py | 2 +- 67 files changed, 204 insertions(+), 173 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c9aa9d18..22caf20b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ line-length = 120 target-version = ['py37', 'py38', 'py39', 'py310', 'py311'] extend-exclude = '^/bin/*' [tool.mypy] +check_untyped_defs = true disable_error_code = [ 'misc', 'import' diff --git a/tabcmd/commands/auth/login_command.py b/tabcmd/commands/auth/login_command.py index d042f724..8d0e9dbf 100644 --- a/tabcmd/commands/auth/login_command.py +++ b/tabcmd/commands/auth/login_command.py @@ -17,9 +17,9 @@ def define_args(parser): # just uses global options pass - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() session.create_session(args, logger) diff --git a/tabcmd/commands/auth/logout_command.py b/tabcmd/commands/auth/logout_command.py index b15f8246..f5848e60 100644 --- a/tabcmd/commands/auth/logout_command.py +++ b/tabcmd/commands/auth/logout_command.py @@ -17,9 +17,9 @@ def define_args(parser): # has no options pass - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() session.end_session_and_clear_data() diff --git a/tabcmd/commands/auth/session.py b/tabcmd/commands/auth/session.py index ba824fca..41becac5 100644 --- a/tabcmd/commands/auth/session.py +++ b/tabcmd/commands/auth/session.py @@ -199,6 +199,9 @@ def _open_connection_with_opts(self) -> TSC.Server: return tableau_server def _verify_server_connection_unauthed(self): + if not self.tableau_server: + Errors.exit_with_error(self.logger, "Attempted to verify non-existent server connection") + return # make typing recognize self.tableau_server is not None after this line try: self.tableau_server.use_server_version() except requests.exceptions.ReadTimeout as timeout_error: @@ -259,10 +262,15 @@ def _validate_existing_signin(self): # server connection created, not yet logged in def _sign_in(self, tableau_auth) -> TSC.Server: - self.logger.debug(_("session.login") + self.server_url) - self.logger.debug(_("listsites.output").format("", self.username or self.token_name, self.site_name)) + self.logger.debug(_("session.login")) + self.logger.info(_("dataconnections.classes.tableau_server_site") + ": {}".format(self._site_display_name())) + # self.logger.debug(_("listsites.output").format("", self.username or self.token_name, self.site_name)) + if not self.tableau_server: + Errors.exit_with_error(self.logger, "Attempted to sign in with no server connection created") + return # make typing recognize self.tableau_server is not None after this line try: - self.tableau_server.auth.sign_in(tableau_auth) # it's the same call for token or user-pass + # it's the same call for token or user-pass + self.tableau_server.auth.sign_in(tableau_auth) except Exception as e: Errors.exit_with_error(self.logger, exception=e) try: @@ -296,7 +304,7 @@ def create_session(self, args, logger): self._read_existing_state() self._update_session_data(args) self.logging_level = args.logging_level or self.logging_level - self.logger = logger or log(__class__.__name__, self.logging_level) + self.logger = logger or log(self.__class__.__name__, self.logging_level) credentials = None if args.password or args.password_file: @@ -358,8 +366,8 @@ def _clear_data(self): self.tableau_server = None self.certificate = None - self.no_certcheck = None - self.no_proxy = None + self.no_certcheck = False + self.no_proxy = True self.proxy = None self.timeout = None @@ -444,7 +452,7 @@ def _save_file(self, data): json.dump(data, f) def _serialize_for_save(self): - data = {"tableau_auth": []} + data: Any = {"tableau_auth": []} data["tableau_auth"].append( { "auth_token": self.auth_token, diff --git a/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py b/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py index fb0d5daf..30ea4776 100644 --- a/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py +++ b/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py @@ -12,9 +12,6 @@ class DatasourcesAndWorkbooks(Server): Base Class for Operations related to Datasources and Workbooks """ - def __init__(self, args): - super().__init__(args) - @staticmethod def get_view_url_from_names(wb_name, view_name): return "{}/sheets/{}".format(wb_name, view_name) diff --git a/tabcmd/commands/datasources_and_workbooks/delete_command.py b/tabcmd/commands/datasources_and_workbooks/delete_command.py index 9ac1a9e3..eaf215fa 100644 --- a/tabcmd/commands/datasources_and_workbooks/delete_command.py +++ b/tabcmd/commands/datasources_and_workbooks/delete_command.py @@ -27,9 +27,9 @@ def define_args(delete_parser): set_project_r_arg(group) set_parent_project_arg(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -74,4 +74,4 @@ def run_command(args): server.datasources.delete(item_to_delete.id) logger.info(_("common.output.succeeded")) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/datasources_and_workbooks/export_command.py b/tabcmd/commands/datasources_and_workbooks/export_command.py index 23012b51..cfecb387 100644 --- a/tabcmd/commands/datasources_and_workbooks/export_command.py +++ b/tabcmd/commands/datasources_and_workbooks/export_command.py @@ -70,9 +70,9 @@ def define_args(export_parser): it to a file. This command can also export just the data used for a view_name """ - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -81,7 +81,7 @@ def run_command(args): if not view_content_url and not wb_content_url: view_example = "/workbook_name/view_name" message = "{} [{}]".format( - _("export.errors.requires_workbook_view_param").format(__class__.__name__), view_example + _("export.errors.requires_workbook_view_param").format(cls.__name__), view_example ) Errors.exit_with_error(logger, message) diff --git a/tabcmd/commands/datasources_and_workbooks/get_url_command.py b/tabcmd/commands/datasources_and_workbooks/get_url_command.py index 38518bd8..4d830f13 100644 --- a/tabcmd/commands/datasources_and_workbooks/get_url_command.py +++ b/tabcmd/commands/datasources_and_workbooks/get_url_command.py @@ -31,14 +31,14 @@ def define_args(get_url_parser): # tabcmd get "/views/Finance/InvestmentGrowth.png?:size=640,480" -f growth.png # tabcmd get "/views/Finance/InvestmentGrowth.png?:refresh=yes" -f growth.png - @staticmethod - def run_command(args): + @classmethod + def run_command(cls, args): # A view can be returned in PDF, PNG, or CSV (summary data only) format. # A Tableau workbook is returned as a TWB if it connects to a datasource/live connection, # or a TWBX if it uses an extract. # A Tableau datasource is returned as a TDS if it connects to a live connection, # or a TDSX if it uses an extract. - logger = log(__class__.__name__, args.logging_level) + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -181,7 +181,7 @@ def generate_pdf(logger, server, args, view_url): filename = GetUrl.filename_from_args(args.filename, view_item.name, "pdf") DatasourcesAndWorkbooks.save_to_file(logger, view_item.pdf, filename) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) @staticmethod def generate_png(logger, server, args, view_url): @@ -195,7 +195,7 @@ def generate_png(logger, server, args, view_url): filename = GetUrl.filename_from_args(args.filename, view_item.name, "png") DatasourcesAndWorkbooks.save_to_file(logger, view_item.image, filename) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) @staticmethod def generate_csv(logger, server, args, view_url): @@ -226,7 +226,7 @@ def generate_twb(logger, server, args, file_extension, url): server.workbooks.download(target_workbook.id, filepath=file_name_with_path, no_extract=False) logger.info(_("export.success").format(target_workbook.name, file_name_with_ext)) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) @staticmethod def generate_tds(logger, server, args, file_extension): @@ -243,4 +243,4 @@ def generate_tds(logger, server, args, file_extension): server.datasources.download(target_datasource.id, filepath=file_name_with_path, no_extract=False) logger.info(_("export.success").format(target_datasource.name, file_name_with_ext)) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index cb50be17..d0df5dd1 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -33,9 +33,9 @@ def define_args(publish_parser): set_append_replace_option(group) set_parent_project_arg(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/datasources_and_workbooks/runschedule_command.py b/tabcmd/commands/datasources_and_workbooks/runschedule_command.py index 9634c4e2..b874f9bc 100644 --- a/tabcmd/commands/datasources_and_workbooks/runschedule_command.py +++ b/tabcmd/commands/datasources_and_workbooks/runschedule_command.py @@ -18,9 +18,9 @@ def define_args(runschedule_parser): group = runschedule_parser.add_argument_group(title=RunSchedule.name) group.add_argument("schedule", help=_("tabcmd.run_schedule.options.schedule")) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/extracts/create_extracts_command.py b/tabcmd/commands/extracts/create_extracts_command.py index 3f39f04a..a0c2baae 100644 --- a/tabcmd/commands/extracts/create_extracts_command.py +++ b/tabcmd/commands/extracts/create_extracts_command.py @@ -26,9 +26,9 @@ def define_args(create_extract_parser): set_project_arg(group) set_parent_project_arg(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -39,9 +39,10 @@ def run_command(args): ) try: item = Extracts.get_wb_or_ds_for_extracts(args, logger, server) + job: TSC.JobItem if args.datasource: logger.info(_("createextracts.for.datasource").format(args.datasource)) - job: TSC.JobItem = server.datasources.create_extract(item, encrypt=args.encrypt) + job = server.datasources.create_extract(item, encrypt=args.encrypt) else: if not args.include_all and not args.embedded_datasources: @@ -51,7 +52,7 @@ def run_command(args): ) logger.info(_("createextracts.for.workbook_name").format(args.workbook)) - job: TSC.JobItem = server.workbooks.create_extract( + job = server.workbooks.create_extract( item, encrypt=args.encrypt, includeAll=args.include_all, @@ -62,7 +63,7 @@ def run_command(args): if args.continue_if_exists and Errors.is_resource_conflict(e): logger.info(_("tabcmd.result.already_exists").format(_("content_type.extract"), args.name)) return - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) logger.info(_("common.output.job_queued_success")) logger.debug("Extract creation queued with JobID: {}".format(job.id)) diff --git a/tabcmd/commands/extracts/decrypt_extracts_command.py b/tabcmd/commands/extracts/decrypt_extracts_command.py index cb47d7fa..8f7eae99 100644 --- a/tabcmd/commands/extracts/decrypt_extracts_command.py +++ b/tabcmd/commands/extracts/decrypt_extracts_command.py @@ -19,9 +19,9 @@ def define_args(decrypt_extract_parser): group = decrypt_extract_parser.add_argument_group(title=DecryptExtracts.name) group.add_argument("site_name", metavar="site-name", help=_("editsite.options.site-name")) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -30,7 +30,7 @@ def run_command(args): logger.info(_("decryptextracts.status").format(args.site_name)) job = server.sites.decrypt_extracts(site_item.id) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) logger.info(_("common.output.job_queued_success")) logger.debug("Extract decryption queued with JobID: {}".format(job.id)) diff --git a/tabcmd/commands/extracts/delete_extracts_command.py b/tabcmd/commands/extracts/delete_extracts_command.py index ab32a458..e9b2981f 100644 --- a/tabcmd/commands/extracts/delete_extracts_command.py +++ b/tabcmd/commands/extracts/delete_extracts_command.py @@ -25,17 +25,18 @@ def define_args(delete_extract_parser): set_project_arg(group) set_parent_project_arg(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) try: item = Extracts.get_wb_or_ds_for_extracts(args, logger, server) + job: TSC.JobItem if args.datasource: logger.info(_("deleteextracts.for.datasource").format(args.datasource)) - job: TSC.JobItem = server.datasources.delete_extract(item) + job = server.datasources.delete_extract(item) else: if not args.include_all and not args.embedded_datasources: Errors.exit_with_error( @@ -43,12 +44,12 @@ def run_command(args): _("extracts.workbook.errors.requires_datasources_or_include_all").format("deleteextracts"), ) logger.info(_("deleteextracts.for.workbook_name").format(args.workbook)) - job: TSC.JobItem = server.workbooks.delete_extract( + job = server.workbooks.delete_extract( item, includeAll=args.include_all, datasources=args.embedded_datasources ) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) logger.info(_("common.output.job_queued_success")) logger.debug("Extract deletion queued with JobID: {}".format(job.id)) diff --git a/tabcmd/commands/extracts/encrypt_extracts_command.py b/tabcmd/commands/extracts/encrypt_extracts_command.py index 0454d0ea..42d70cbd 100644 --- a/tabcmd/commands/extracts/encrypt_extracts_command.py +++ b/tabcmd/commands/extracts/encrypt_extracts_command.py @@ -21,9 +21,9 @@ def define_args(encrypt_extract_parser): group = encrypt_extract_parser.add_argument_group(title=EncryptExtracts.name) group.add_argument("site_name", metavar="site-name", help=_("editsite.options.site-name")) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -32,7 +32,7 @@ def run_command(args): logger.info(_("encryptextracts.status").format(site_item.name)) job = server.sites.encrypt_extracts(site_item.id) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) logger.info(_("common.output.job_queued_success")) logger.debug("Extract encryption queued with JobID: {}".format(job.id)) diff --git a/tabcmd/commands/extracts/extracts.py b/tabcmd/commands/extracts/extracts.py index 60c63c00..50729dbc 100644 --- a/tabcmd/commands/extracts/extracts.py +++ b/tabcmd/commands/extracts/extracts.py @@ -18,12 +18,11 @@ def get_wb_or_ds_for_extracts(args, logger, server): return datasource elif args.workbook or args.url: + workbook_item: TSC.WorkbookItem if args.workbook: - workbook_item: TSC.WorkbookItem = Server.get_workbook_item(logger, server, args.workbook, container) + workbook_item = Server.get_workbook_item(logger, server, args.workbook, container) else: - workbook_item: TSC.WorkbookItem = DatasourcesAndWorkbooks.get_wb_by_content_url( - logger, server, args.url - ) + workbook_item = DatasourcesAndWorkbooks.get_wb_by_content_url(logger, server, args.url) logger.info(_("export.status").format(workbook_item.name)) return workbook_item diff --git a/tabcmd/commands/extracts/reencrypt_extracts_command.py b/tabcmd/commands/extracts/reencrypt_extracts_command.py index e8eb363a..3026fff9 100644 --- a/tabcmd/commands/extracts/reencrypt_extracts_command.py +++ b/tabcmd/commands/extracts/reencrypt_extracts_command.py @@ -21,9 +21,9 @@ def define_args(reencrypt_extract_parser): group = reencrypt_extract_parser.add_argument_group(title=ReencryptExtracts.name) group.add_argument("site_name", metavar="site-name", help=_("editsite.options.site-name")) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -32,7 +32,7 @@ def run_command(args): logger.info(_("reencryptextracts.status").format(site_item.name)) job = server.sites.encrypt_extracts(site_item.id) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) logger.info(_("common.output.job_queued_success")) logger.debug("Extract re-encryption queued with JobID: {}".format(job.id)) diff --git a/tabcmd/commands/extracts/refresh_extracts_command.py b/tabcmd/commands/extracts/refresh_extracts_command.py index b88f220b..4d8aadc2 100644 --- a/tabcmd/commands/extracts/refresh_extracts_command.py +++ b/tabcmd/commands/extracts/refresh_extracts_command.py @@ -23,9 +23,9 @@ def define_args(refresh_extract_parser): set_project_arg(group) set_parent_project_arg(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -42,11 +42,12 @@ def run_command(args): try: item = Extracts.get_wb_or_ds_for_extracts(args, logger, server) + job: TSC.JobItem if args.datasource: logger.info(_("refreshextracts.status_refreshed").format(_("content_type.datasource"), args.datasource)) - job: TSC.JobItem = server.datasources.refresh(item.id) + job = server.datasources.refresh(item.id) else: - job: TSC.JobItem = server.workbooks.refresh(item.id) + job = server.workbooks.refresh(item.id) logger.info(_("refreshextracts.status_refreshed").format(_("content_type.workbook"), args.workbook)) except Exception as e: @@ -64,4 +65,4 @@ def run_command(args): logger.info("Job completed: ") logger.info(job_done) except Exception as je: - Errors.exit_with_error(logger, je) + Errors.exit_with_error(logger, exception=je) diff --git a/tabcmd/commands/group/create_group_command.py b/tabcmd/commands/group/create_group_command.py index 85efcaae..36950824 100644 --- a/tabcmd/commands/group/create_group_command.py +++ b/tabcmd/commands/group/create_group_command.py @@ -20,9 +20,9 @@ def define_args(create_group_parser): args_group = create_group_parser.add_argument_group(title=CreateGroupCommand.name) args_group.add_argument("name") - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/group/delete_group_command.py b/tabcmd/commands/group/delete_group_command.py index 71af42a5..5937c08c 100644 --- a/tabcmd/commands/group/delete_group_command.py +++ b/tabcmd/commands/group/delete_group_command.py @@ -20,9 +20,9 @@ def define_args(delete_group_parser): args_group = delete_group_parser.add_argument_group(title=DeleteGroupCommand.name) args_group.add_argument("name") - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/project/create_project_command.py b/tabcmd/commands/project/create_project_command.py index 85853edd..5b8c4674 100644 --- a/tabcmd/commands/project/create_project_command.py +++ b/tabcmd/commands/project/create_project_command.py @@ -25,9 +25,9 @@ def define_args(create_project_parser): set_parent_project_arg(create_project_parser) set_description_arg(create_project_parser) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -37,8 +37,8 @@ def run_command(args): try: logger.info(_("tabcmd.find.parent_project").format(args.parent_project_path)) parent = Server.get_project_by_name_and_parent_path(logger, server, None, args.parent_project_path) - except Exception as exc: - Errors.exit_with_error(logger, exc) + except Exception as e: + Errors.exit_with_error(logger, exception=e) readable_name = "{0}/{1}".format(args.parent_project_path, args.project_name) parent_id = parent.id logger.debug("parent project = `{0}`, id = {1}".format(args.parent_project_path, parent_id)) @@ -54,6 +54,6 @@ def run_command(args): logger.info(_("tabcmd.result.already_exists").format(_("content_type.project"), args.project_name)) logger.info(_("common.output.succeeded")) else: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) return project_item diff --git a/tabcmd/commands/project/delete_project_command.py b/tabcmd/commands/project/delete_project_command.py index b107406c..422e504e 100644 --- a/tabcmd/commands/project/delete_project_command.py +++ b/tabcmd/commands/project/delete_project_command.py @@ -22,9 +22,9 @@ def define_args(delete_project_parser): args_group.add_argument("project_name", metavar="project-name", help=_("createproject.options.name")) set_parent_project_arg(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -37,7 +37,7 @@ def run_command(args): logger, server, args.project_name, args.parent_project_path ) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) project_id = project.id try: diff --git a/tabcmd/commands/project/publish_samples_command.py b/tabcmd/commands/project/publish_samples_command.py index 046e4491..d46f305a 100644 --- a/tabcmd/commands/project/publish_samples_command.py +++ b/tabcmd/commands/project/publish_samples_command.py @@ -26,9 +26,9 @@ def define_args(publish_samples_parser): ) set_parent_project_arg(args_group) # args.parent_project_name - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/server.py b/tabcmd/commands/server.py index d315e6dd..b67b4ad2 100644 --- a/tabcmd/commands/server.py +++ b/tabcmd/commands/server.py @@ -113,7 +113,9 @@ def get_filename_extension_if_tableau_type(logger, filename): ) @staticmethod - def get_project_by_name_and_parent_path(logger, server, project_name: str, parent_path: str) -> TSC.ProjectItem: + def get_project_by_name_and_parent_path( + logger, server, project_name: Optional[str], parent_path: Optional[str] + ) -> TSC.ProjectItem: logger.debug(_("content_type.project") + ":{0}, {1}".format(parent_path, project_name)) if not parent_path: if not project_name: @@ -135,7 +137,7 @@ def get_project_by_name_and_parent_path(logger, server, project_name: str, paren return project @staticmethod - def _parse_project_path_to_list(project_path: str): + def _parse_project_path_to_list(project_path: Optional[str]): if project_path is None or project_path == "": return [] if project_path.find("/") == -1: diff --git a/tabcmd/commands/site/create_site_command.py b/tabcmd/commands/site/create_site_command.py index f684ba1f..947534d9 100644 --- a/tabcmd/commands/site/create_site_command.py +++ b/tabcmd/commands/site/create_site_command.py @@ -22,9 +22,9 @@ def define_args(create_site_parser): args_group.add_argument("new_site_name", metavar="site-name", help=_("editsite.options.site-name")) set_common_site_args(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -46,4 +46,4 @@ def run_command(args): if Errors.is_resource_conflict(e) and args.continue_if_exists: logger.info(_("createsite.errors.site_name_already_exists").format(args.new_site_name)) return - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/site/delete_site_command.py b/tabcmd/commands/site/delete_site_command.py index f1892730..a6bbf851 100644 --- a/tabcmd/commands/site/delete_site_command.py +++ b/tabcmd/commands/site/delete_site_command.py @@ -20,9 +20,9 @@ def define_args(delete_site_parser): args_group = delete_site_parser.add_argument_group(title=DeleteSiteCommand.name) args_group.add_argument("site_name_to_delete", metavar="site-name", help=strings[2]) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/site/edit_site_command.py b/tabcmd/commands/site/edit_site_command.py index ab6451ed..8b92a8ae 100644 --- a/tabcmd/commands/site/edit_site_command.py +++ b/tabcmd/commands/site/edit_site_command.py @@ -25,9 +25,9 @@ def define_args(edit_site_parser): set_common_site_args(args_group) set_site_status_arg(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -47,4 +47,4 @@ def run_command(args): logger.info(_("common.output.succeeded")) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/site/list_command.py b/tabcmd/commands/site/list_command.py index 64e4fa48..b33be9c1 100644 --- a/tabcmd/commands/site/list_command.py +++ b/tabcmd/commands/site/list_command.py @@ -29,8 +29,8 @@ def define_args(list_parser): ) args_group.add_argument("-d", "--details", action="store_true", help="Show object details") - @staticmethod - def run_command(args): + @classmethod + def run_command(cls, args): logger = log(__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() @@ -63,4 +63,4 @@ def run_command(args): logger.info(ListCommand.local_strings["tabcmd_listing_label_name"].format(item.name)) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/site/list_sites_command.py b/tabcmd/commands/site/list_sites_command.py index f124fbe6..f3612c46 100644 --- a/tabcmd/commands/site/list_sites_command.py +++ b/tabcmd/commands/site/list_sites_command.py @@ -21,9 +21,9 @@ def define_args(list_site_parser): group = list_site_parser.add_argument_group(title=ListSiteCommand.name) set_site_detail_option(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -36,4 +36,4 @@ def run_command(args): if args.get_extract_encryption_mode: logger.info("EXTRACTENCRYPTION:", site.extract_encryption_mode) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/user/add_users_command.py b/tabcmd/commands/user/add_users_command.py index 1fc79b46..8290d0b4 100644 --- a/tabcmd/commands/user/add_users_command.py +++ b/tabcmd/commands/user/add_users_command.py @@ -20,9 +20,9 @@ def define_args(add_user_parser): set_users_file_arg(args_group) set_completeness_options(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/user/create_site_users.py b/tabcmd/commands/user/create_site_users.py index ce515fec..0382d33d 100644 --- a/tabcmd/commands/user/create_site_users.py +++ b/tabcmd/commands/user/create_site_users.py @@ -26,9 +26,9 @@ def define_args(create_site_users_parser): set_completeness_options(args_group) UserCommand.set_auth_arg(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/user/create_users_command.py b/tabcmd/commands/user/create_users_command.py index 1c3ab4fa..317190a7 100644 --- a/tabcmd/commands/user/create_users_command.py +++ b/tabcmd/commands/user/create_users_command.py @@ -26,9 +26,9 @@ def define_args(create_users_parser): set_completeness_options(args_group) UserCommand.set_auth_arg(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/user/delete_site_users_command.py b/tabcmd/commands/user/delete_site_users_command.py index 731ed7bf..bc6fcc91 100644 --- a/tabcmd/commands/user/delete_site_users_command.py +++ b/tabcmd/commands/user/delete_site_users_command.py @@ -23,9 +23,9 @@ def define_args(delete_site_users_parser): set_users_file_positional(args_group) set_completeness_options(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/user/remove_users_command.py b/tabcmd/commands/user/remove_users_command.py index 38f0f720..9652d9b4 100644 --- a/tabcmd/commands/user/remove_users_command.py +++ b/tabcmd/commands/user/remove_users_command.py @@ -20,9 +20,9 @@ def define_args(remove_users_parser): set_users_file_arg(args_group) set_completeness_options(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/execution/logger_config.py b/tabcmd/execution/logger_config.py index 17fe6907..f83f2b59 100644 --- a/tabcmd/execution/logger_config.py +++ b/tabcmd/execution/logger_config.py @@ -41,7 +41,7 @@ def add_trace_level(): FORMATS[trace_level] = FORMATS[logging.ERROR] -def configure_log(name: str, logging_level_input: str): +def configure_log(name: str, logging_level_input: str) -> logging.Logger: """function for logging statements to console and logfile""" logging_level = getattr(logging, logging_level_input.upper()) log_format = FORMATS[logging_level] @@ -61,5 +61,5 @@ def configure_log(name: str, logging_level_input: str): def log(file_name, logging_level): logger = configure_log(file_name, logging_level) if not hasattr(logger, "trace"): - logger.trace = logger.debug + logger.trace = logger.debug # type: ignore return logger diff --git a/tabcmd/execution/parent_parser.py b/tabcmd/execution/parent_parser.py index bc81f59c..4332abec 100644 --- a/tabcmd/execution/parent_parser.py +++ b/tabcmd/execution/parent_parser.py @@ -190,7 +190,7 @@ def __init__(self, _parser: ParentParser): def run_command(self, args): logger = log(__name__, "info") logger.info(strings[6] + " " + version + "\n") - logger.info(self.parser.root.format_help()) + logger.info(self.parser.root.format_help()) # type: ignore strings = [ diff --git a/tabcmd/tabcmd.py b/tabcmd/tabcmd.py index 70b8b63d..ec0fe00a 100644 --- a/tabcmd/tabcmd.py +++ b/tabcmd/tabcmd.py @@ -17,13 +17,14 @@ def main(): print("Keyboard Interrupt: exiting") sys.exit(1) except Exception as e: - sys.stderr.writelines( - [ - "ERROR\n", - "Unhandled exception: {}\n".format(type(e).__name__), - f"at line {e.__traceback__.tb_lineno} of {__file__}: {e}\n", - ] - ) + if e.__traceback__: + sys.stderr.writelines( + [ + "ERROR\n", + "Unhandled exception: {}\n".format(type(e).__name__), + f"at line {e.__traceback__.tb_lineno} of {__file__}: {e}\n", + ] + ) sys.exit(1) diff --git a/tests/commands/test_session.py b/tests/commands/test_session.py index cb60260d..4d2118ff 100644 --- a/tests/commands/test_session.py +++ b/tests/commands/test_session.py @@ -156,7 +156,7 @@ def test__create_new_username_credential_succeeds_new_password(self, mock_pass): test_password = "pword1" active_session = Session() active_session.username = "user" - active_session.site = "" + active_session.site_name = "" auth = active_session._create_new_credential(test_password, Session.PASSWORD_CRED_TYPE) assert auth is not None @@ -175,7 +175,7 @@ def test__create_new_token_credential_succeeds_from_self(self, mock_pass): def test__create_new_username_credential_succeeds_from_self(self, mock_pass): active_session = Session() active_session.username = "user3" - active_session.site = "" + active_session.site_name = "" auth = active_session._create_new_credential(None, Session.PASSWORD_CRED_TYPE) assert mock_pass.has_been_called() assert auth is not None @@ -201,18 +201,24 @@ def test__create_new_username_credential_succeeds_from_args(self, mock_pass): class PromptingTests(unittest.TestCase): def test_show_prompt_if_user_didnt_say(self): + session: Session = Session() test_args = Namespace(**vars(args_to_mock)) - assert Session._allow_prompt(test_args) is True, test_args + session._update_session_data(test_args) + assert session._allow_prompt() is True, test_args def test_show_prompt_if_user_said_yes(self): + session: Session = Session() test_args = Namespace(**vars(args_to_mock)) test_args.prompt = True - assert Session._allow_prompt(test_args) is True, test_args + session._update_session_data(test_args) + assert session._allow_prompt() is True, test_args def test_dont_show_prompt_if_user_said_no(self): + session: Session = Session() test_args = Namespace(**vars(args_to_mock)) test_args.no_prompt = True - assert Session._allow_prompt(test_args) is False, test_args + session._update_session_data(test_args) + assert session._allow_prompt() is False, test_args """ diff --git a/tests/e2e/language_tests.py b/tests/e2e/language_tests.py index 6fced7a3..9d45673d 100644 --- a/tests/e2e/language_tests.py +++ b/tests/e2e/language_tests.py @@ -101,6 +101,12 @@ def _get_datasource(self, server_file): arguments = [command, server_file] _test_command(arguments) + def _get_workbook(self, server_file): + command = "get" + server_file = "/workbooks/" + server_file + arguments = [command, server_file] + _test_command(arguments) + def _get_custom_view(self): command = "get" diff --git a/tests/e2e/tests_integration.py b/tests/e2e/tests_integration.py index f63588b1..72c26523 100644 --- a/tests/e2e/tests_integration.py +++ b/tests/e2e/tests_integration.py @@ -1,7 +1,9 @@ import argparse import logging import pytest +import tableauserverclient as TSC import unittest + from tabcmd.commands.auth.session import Session from tabcmd.commands.server import Server from tabcmd.execution.logger_config import log @@ -13,6 +15,7 @@ credentials = None # type: ignore fakeserver = "http://SRVR" +logger: logging.Logger logging.disable(logging.ERROR) # these are integration tests because they don't just run a command, they call interior methods @@ -130,9 +133,8 @@ def test_read_password_file(self): # test_session.create_session(args) def test_get_project(self): - logger = log(__class__.__name__, "info") server = E2EServerTests.test_log_in() - Server.get_project_by_name_and_parent_path(logger, server, "Default", None) + project: TSC.ProjectItem = Server.get_project_by_name_and_parent_path(logger, server, "Default", None) logging.disable(logging.NOTSET) diff --git a/tests/parsers/common_setup.py b/tests/parsers/common_setup.py index 619d4805..40c1a481 100644 --- a/tests/parsers/common_setup.py +++ b/tests/parsers/common_setup.py @@ -1,7 +1,9 @@ +import argparse +import unittest + from tabcmd.execution import parent_parser from collections import namedtuple - encoding = "utf-8-sig" @@ -13,7 +15,7 @@ def mock_command_action(): def initialize_test_pieces(commandname, command_object): manager = parent_parser.ParentParser() parser = manager.get_root_parser() - mock_command = namedtuple("TestObject", "name, run_command, description, define_args") + mock_command = namedtuple("mock_command", "name, run_command, description, define_args") mock_command.name = commandname mock_command.run_command = mock_command_action mock_command.description = "mock help text" @@ -31,3 +33,7 @@ def initialize_test_pieces(commandname, command_object): bad mix of optional arguments has unknown arguments """ + + +class ParserTestCase(unittest.TestCase): + parser_under_test: argparse.ArgumentParser diff --git a/tests/parsers/test_login_parser.py b/tests/parsers/test_login_parser.py index 2276ef45..23a055ba 100644 --- a/tests/parsers/test_login_parser.py +++ b/tests/parsers/test_login_parser.py @@ -8,7 +8,7 @@ @mock.patch("sys.argv", None) -class LoginParserTest(unittest.TestCase): +class LoginParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, LoginCommand) diff --git a/tests/parsers/test_logout_parser.py b/tests/parsers/test_logout_parser.py index 5d59eab7..09672d15 100644 --- a/tests/parsers/test_logout_parser.py +++ b/tests/parsers/test_logout_parser.py @@ -8,7 +8,7 @@ commandname = "logout" -class LogoutParserTest(unittest.TestCase): +class LogoutParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, LogoutCommand) diff --git a/tests/parsers/test_parser_add_user.py b/tests/parsers/test_parser_add_user.py index fe57f669..ad9ee455 100644 --- a/tests/parsers/test_parser_add_user.py +++ b/tests/parsers/test_parser_add_user.py @@ -7,7 +7,7 @@ commandname = "addusers" -class AddUsersParserTest(unittest.TestCase): +class AddUsersParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, AddUserCommand) @@ -26,7 +26,7 @@ def test_add_users_parser_users_file(self): with mock.patch("builtins.open", mock.mock_open(read_data="test")) as open_file: mock_args = [commandname, "group-name", "--users", "users.csv"] args = self.parser_under_test.parse_args(mock_args) - self.assertEqual(args.name, "group-name"), args + self.assertEqual(args.name, "group-name", args) open_file.assert_called_with("users.csv", "r", -1, encoding, None), args @mock.patch("builtins.open") diff --git a/tests/parsers/test_parser_create_extracts.py b/tests/parsers/test_parser_create_extracts.py index 8a6d3f49..38fddc4a 100644 --- a/tests/parsers/test_parser_create_extracts.py +++ b/tests/parsers/test_parser_create_extracts.py @@ -6,7 +6,7 @@ commandname = "createextracts" -class CreateExtractsParserTest(unittest.TestCase): +class CreateExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateExtracts) diff --git a/tests/parsers/test_parser_create_group.py b/tests/parsers/test_parser_create_group.py index de05934d..4b5b32e5 100644 --- a/tests/parsers/test_parser_create_group.py +++ b/tests/parsers/test_parser_create_group.py @@ -6,7 +6,7 @@ commandname = "creategroup" -class CreateGroupParserTest(unittest.TestCase): +class CreateGroupParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateGroupCommand) diff --git a/tests/parsers/test_parser_create_project.py b/tests/parsers/test_parser_create_project.py index b02319c6..3fb6a57f 100644 --- a/tests/parsers/test_parser_create_project.py +++ b/tests/parsers/test_parser_create_project.py @@ -6,7 +6,7 @@ commandname = "createproject" -class CreateProjectParserTest(unittest.TestCase): +class CreateProjectParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateProjectCommand) diff --git a/tests/parsers/test_parser_create_site.py b/tests/parsers/test_parser_create_site.py index ebe08813..bbf08fa7 100644 --- a/tests/parsers/test_parser_create_site.py +++ b/tests/parsers/test_parser_create_site.py @@ -6,7 +6,7 @@ commandname = "createsite" -class CreateSiteParserTest(unittest.TestCase): +class CreateSiteParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateSiteCommand) diff --git a/tests/parsers/test_parser_create_site_users.py b/tests/parsers/test_parser_create_site_users.py index 5b46bd00..e7f80a81 100644 --- a/tests/parsers/test_parser_create_site_users.py +++ b/tests/parsers/test_parser_create_site_users.py @@ -7,7 +7,7 @@ commandname = "createsiteusers" -class CreateSiteUsersParserTest(unittest.TestCase): +class CreateSiteUsersParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateSiteUsersCommand) diff --git a/tests/parsers/test_parser_create_user.py b/tests/parsers/test_parser_create_user.py index 7b67f24f..7240c0ce 100644 --- a/tests/parsers/test_parser_create_user.py +++ b/tests/parsers/test_parser_create_user.py @@ -7,7 +7,7 @@ commandname = "createusers" -class CreateUsersTest(unittest.TestCase): +class CreateUsersTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateUsersCommand) diff --git a/tests/parsers/test_parser_decrypt_extracts.py b/tests/parsers/test_parser_decrypt_extracts.py index 1fdd5dc2..02f79e43 100644 --- a/tests/parsers/test_parser_decrypt_extracts.py +++ b/tests/parsers/test_parser_decrypt_extracts.py @@ -6,7 +6,7 @@ commandname = "decryptextracts" -class DecryptExtractsParserTest(unittest.TestCase): +class DecryptExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DecryptExtracts) diff --git a/tests/parsers/test_parser_delete.py b/tests/parsers/test_parser_delete.py index 9d6900d0..5747befb 100644 --- a/tests/parsers/test_parser_delete.py +++ b/tests/parsers/test_parser_delete.py @@ -6,7 +6,7 @@ commandname = "delete" -class DeleteParserTest(unittest.TestCase): +class DeleteParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DeleteCommand) diff --git a/tests/parsers/test_parser_delete_extracts.py b/tests/parsers/test_parser_delete_extracts.py index 1d71d187..f7e9e162 100644 --- a/tests/parsers/test_parser_delete_extracts.py +++ b/tests/parsers/test_parser_delete_extracts.py @@ -6,7 +6,7 @@ commandname = "deleteextracts" -class DeleteExtractsParserTest(unittest.TestCase): +class DeleteExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DeleteExtracts) diff --git a/tests/parsers/test_parser_delete_group.py b/tests/parsers/test_parser_delete_group.py index 368e3cce..887230da 100644 --- a/tests/parsers/test_parser_delete_group.py +++ b/tests/parsers/test_parser_delete_group.py @@ -6,7 +6,7 @@ commandname = "deletegroup" -class DeleteGroupParserTestT(unittest.TestCase): +class DeleteGroupParserTestT(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DeleteGroupCommand) diff --git a/tests/parsers/test_parser_delete_project.py b/tests/parsers/test_parser_delete_project.py index 5ecf43bc..5291b6c9 100644 --- a/tests/parsers/test_parser_delete_project.py +++ b/tests/parsers/test_parser_delete_project.py @@ -6,7 +6,7 @@ commandname = "deleteproject" -class DeleteProjectParserTest(unittest.TestCase): +class DeleteProjectParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DeleteProjectCommand) diff --git a/tests/parsers/test_parser_delete_site.py b/tests/parsers/test_parser_delete_site.py index 2d9562f7..7a82cd90 100644 --- a/tests/parsers/test_parser_delete_site.py +++ b/tests/parsers/test_parser_delete_site.py @@ -6,7 +6,7 @@ commandname = "deletesite" -class DeleteSiteParserTest(unittest.TestCase): +class DeleteSiteParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DeleteSiteCommand) diff --git a/tests/parsers/test_parser_delete_site_user.py b/tests/parsers/test_parser_delete_site_user.py index c0d81b7e..c61103ff 100644 --- a/tests/parsers/test_parser_delete_site_user.py +++ b/tests/parsers/test_parser_delete_site_user.py @@ -7,7 +7,7 @@ commandname = "deletesiteusers" -class DeleteSiteUsersParserTest(unittest.TestCase): +class DeleteSiteUsersParserTest(ParserTestCase): csv = ("testname", "testpassword", "test", "test", "test", "test") @classmethod diff --git a/tests/parsers/test_parser_edit_site.py b/tests/parsers/test_parser_edit_site.py index 447deecf..d36223d5 100644 --- a/tests/parsers/test_parser_edit_site.py +++ b/tests/parsers/test_parser_edit_site.py @@ -6,7 +6,7 @@ commandname = "editsites" -class EditSiteParserTest(unittest.TestCase): +class EditSiteParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, EditSiteCommand) diff --git a/tests/parsers/test_parser_encrypt_extracts.py b/tests/parsers/test_parser_encrypt_extracts.py index e87f8f2a..7e170137 100644 --- a/tests/parsers/test_parser_encrypt_extracts.py +++ b/tests/parsers/test_parser_encrypt_extracts.py @@ -6,7 +6,7 @@ commandname = "encryptextracts" -class EncryptExtractsParserTest(unittest.TestCase): +class EncryptExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, EncryptExtracts) diff --git a/tests/parsers/test_parser_export.py b/tests/parsers/test_parser_export.py index f90999a9..542276f2 100644 --- a/tests/parsers/test_parser_export.py +++ b/tests/parsers/test_parser_export.py @@ -6,7 +6,7 @@ commandname = "export" -class ExportParserTest(unittest.TestCase): +class ExportParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, ExportCommand) diff --git a/tests/parsers/test_parser_get_url.py b/tests/parsers/test_parser_get_url.py index 5467e236..fa07c1fa 100644 --- a/tests/parsers/test_parser_get_url.py +++ b/tests/parsers/test_parser_get_url.py @@ -6,13 +6,13 @@ commandname = "listsites" -class GetUrlParserTest(unittest.TestCase): +class GetUrlParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, GetUrl) def test_get_url_parser_file(self): - mock_args = vars(argparse.Namespace(filename="helloworld")) + mock_args = list(vars(argparse.Namespace(filename="helloworld"))) with self.assertRaises(SystemExit): args = self.parser_under_test.parse_args(mock_args) diff --git a/tests/parsers/test_parser_list_sites.py b/tests/parsers/test_parser_list_sites.py index 71b01234..1c1e448f 100644 --- a/tests/parsers/test_parser_list_sites.py +++ b/tests/parsers/test_parser_list_sites.py @@ -6,7 +6,7 @@ commandname = "listsites" -class ListSitesParserTest(unittest.TestCase): +class ListSitesParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, ListSiteCommand) diff --git a/tests/parsers/test_parser_publish.py b/tests/parsers/test_parser_publish.py index 65aaa20d..e3fc263c 100644 --- a/tests/parsers/test_parser_publish.py +++ b/tests/parsers/test_parser_publish.py @@ -7,7 +7,7 @@ commandname = "Publish" -class PublishParserTest(unittest.TestCase): +class PublishParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, PublishCommand) diff --git a/tests/parsers/test_parser_publish_samples.py b/tests/parsers/test_parser_publish_samples.py index 43c4062a..36fef9f2 100644 --- a/tests/parsers/test_parser_publish_samples.py +++ b/tests/parsers/test_parser_publish_samples.py @@ -6,7 +6,7 @@ commandname = "publishsamples" -class PublishSamplesParserTest(unittest.TestCase): +class PublishSamplesParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, PublishSamplesCommand) diff --git a/tests/parsers/test_parser_reencrypt_extracts.py b/tests/parsers/test_parser_reencrypt_extracts.py index 79d82cda..e919ea70 100644 --- a/tests/parsers/test_parser_reencrypt_extracts.py +++ b/tests/parsers/test_parser_reencrypt_extracts.py @@ -6,7 +6,7 @@ commandname = "reencryptextracts" -class ReencryptExtractsParserTest(unittest.TestCase): +class ReencryptExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, ReencryptExtracts) diff --git a/tests/parsers/test_parser_refresh_extracts.py b/tests/parsers/test_parser_refresh_extracts.py index a74ac760..10d5e5e8 100644 --- a/tests/parsers/test_parser_refresh_extracts.py +++ b/tests/parsers/test_parser_refresh_extracts.py @@ -6,7 +6,7 @@ commandname = "refreshextracts" -class RefreshExtractsParserTest(unittest.TestCase): +class RefreshExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, RefreshExtracts) diff --git a/tests/parsers/test_parser_remove_user.py b/tests/parsers/test_parser_remove_user.py index 6441354a..e5650af8 100644 --- a/tests/parsers/test_parser_remove_user.py +++ b/tests/parsers/test_parser_remove_user.py @@ -7,7 +7,7 @@ commandname = "removeusers" -class RemoveUsersParserTest(unittest.TestCase): +class RemoveUsersParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, RemoveUserCommand) diff --git a/tests/parsers/test_parser_runschedule.py b/tests/parsers/test_parser_runschedule.py index 445dfb58..5d6a174d 100644 --- a/tests/parsers/test_parser_runschedule.py +++ b/tests/parsers/test_parser_runschedule.py @@ -6,7 +6,7 @@ commandname = "runschedule" -class RunScheduleParserTest(unittest.TestCase): +class RunScheduleParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, RunSchedule) From 5707047b082172e0806e54306474527e444f6d51 Mon Sep 17 00:00:00 2001 From: Jac Date: Sat, 11 Mar 2023 14:21:49 -0800 Subject: [PATCH 06/10] Jac/parse publish options (#236) * implement thumbnail options. (including 'not yet implemented' message for --thumbnail-group) * Implement overwrite/append/replace arguments (including not-yet-implemented message for --replace) * clarified help content for extract-encryption and some other not-implemented options * added tests for all valid publish command options * error log cleanup: better stacktrace, less repetition --- tabcmd/commands/datasources_and_workbooks/publish_command.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index a4ff8c26..ca59f925 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -72,6 +72,7 @@ def run_command(args): new_workbook = TSC.WorkbookItem(project_id, name=args.name, show_tabs=args.tabbed) try: + print(creds) new_workbook = server.workbooks.publish( new_workbook, args.filename, From 705646c82fa26413c9cb756e3b217435147b8263 Mon Sep 17 00:00:00 2001 From: Jac Date: Thu, 27 Jul 2023 00:29:00 -0700 Subject: [PATCH 07/10] Remove duplicated tests in merge Also change a print statement to log statement --- tabcmd/commands/datasources_and_workbooks/publish_command.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index ca59f925..3b567d52 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -72,7 +72,8 @@ def run_command(args): new_workbook = TSC.WorkbookItem(project_id, name=args.name, show_tabs=args.tabbed) try: - print(creds) + if creds: + logger.debug("Workbook credentials object: " + creds) new_workbook = server.workbooks.publish( new_workbook, args.filename, From 4f1693fb97edb07c496558aa6e95dd97514a4d0e Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 17 Aug 2023 02:04:15 -0700 Subject: [PATCH 08/10] enable type checking everywhere (cherry picked from commit ef93fff1280288a41e9eaf4764faefc30befd9d1) --- pyproject.toml | 1 + tabcmd/commands/auth/login_command.py | 6 ++--- tabcmd/commands/auth/logout_command.py | 6 ++--- tabcmd/commands/auth/session.py | 22 +++++++++++++------ .../datasources_and_workbooks_command.py | 3 --- .../delete_command.py | 8 +++---- .../export_command.py | 8 +++---- .../get_url_command.py | 6 ++--- .../publish_command.py | 6 ++--- .../runschedule_command.py | 6 ++--- .../extracts/create_extracts_command.py | 13 ++++++----- .../extracts/decrypt_extracts_command.py | 8 +++---- .../extracts/delete_extracts_command.py | 13 ++++++----- .../extracts/encrypt_extracts_command.py | 8 +++---- tabcmd/commands/extracts/extracts.py | 7 +++--- .../extracts/reencrypt_extracts_command.py | 8 +++---- .../extracts/refresh_extracts_command.py | 13 ++++++----- tabcmd/commands/group/create_group_command.py | 6 ++--- tabcmd/commands/group/delete_group_command.py | 6 ++--- .../project/create_project_command.py | 12 +++++----- .../project/delete_project_command.py | 8 +++---- .../project/publish_samples_command.py | 6 ++--- tabcmd/commands/server.py | 6 +++-- tabcmd/commands/site/create_site_command.py | 8 +++---- tabcmd/commands/site/delete_site_command.py | 6 ++--- tabcmd/commands/site/edit_site_command.py | 8 +++---- tabcmd/commands/site/list_command.py | 6 ++--- tabcmd/commands/site/list_sites_command.py | 8 +++---- tabcmd/commands/user/add_users_command.py | 6 ++--- tabcmd/commands/user/create_site_users.py | 6 ++--- tabcmd/commands/user/create_users_command.py | 6 ++--- .../user/delete_site_users_command.py | 6 ++--- tabcmd/commands/user/remove_users_command.py | 6 ++--- tabcmd/execution/logger_config.py | 4 ++-- tabcmd/execution/parent_parser.py | 2 +- tabcmd/tabcmd.py | 15 +++++++------ tests/commands/test_session.py | 16 +++++++++----- tests/e2e/language_tests.py | 6 +++++ tests/e2e/tests_integration.py | 6 +++-- tests/parsers/common_setup.py | 10 +++++++-- tests/parsers/test_login_parser.py | 2 +- tests/parsers/test_logout_parser.py | 2 +- tests/parsers/test_parser_add_user.py | 4 ++-- tests/parsers/test_parser_create_extracts.py | 2 +- tests/parsers/test_parser_create_group.py | 2 +- tests/parsers/test_parser_create_project.py | 2 +- tests/parsers/test_parser_create_site.py | 2 +- .../parsers/test_parser_create_site_users.py | 2 +- tests/parsers/test_parser_create_user.py | 2 +- tests/parsers/test_parser_decrypt_extracts.py | 2 +- tests/parsers/test_parser_delete.py | 2 +- tests/parsers/test_parser_delete_extracts.py | 2 +- tests/parsers/test_parser_delete_group.py | 2 +- tests/parsers/test_parser_delete_project.py | 2 +- tests/parsers/test_parser_delete_site.py | 2 +- tests/parsers/test_parser_delete_site_user.py | 2 +- tests/parsers/test_parser_edit_site.py | 2 +- tests/parsers/test_parser_encrypt_extracts.py | 2 +- tests/parsers/test_parser_export.py | 2 +- tests/parsers/test_parser_get_url.py | 4 ++-- tests/parsers/test_parser_list_sites.py | 2 +- tests/parsers/test_parser_publish.py | 2 +- tests/parsers/test_parser_publish_samples.py | 2 +- .../parsers/test_parser_reencrypt_extracts.py | 2 +- tests/parsers/test_parser_refresh_extracts.py | 2 +- tests/parsers/test_parser_remove_user.py | 2 +- tests/parsers/test_parser_runschedule.py | 2 +- 67 files changed, 200 insertions(+), 169 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 00a6ed92..f3d0ac65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ required-version = 22 target-version = ['py38', 'py39', 'py310', 'py311'] extend-exclude = '^/bin/*' [tool.mypy] +check_untyped_defs = true disable_error_code = [ 'misc', 'import' diff --git a/tabcmd/commands/auth/login_command.py b/tabcmd/commands/auth/login_command.py index d042f724..8d0e9dbf 100644 --- a/tabcmd/commands/auth/login_command.py +++ b/tabcmd/commands/auth/login_command.py @@ -17,9 +17,9 @@ def define_args(parser): # just uses global options pass - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() session.create_session(args, logger) diff --git a/tabcmd/commands/auth/logout_command.py b/tabcmd/commands/auth/logout_command.py index b15f8246..f5848e60 100644 --- a/tabcmd/commands/auth/logout_command.py +++ b/tabcmd/commands/auth/logout_command.py @@ -17,9 +17,9 @@ def define_args(parser): # has no options pass - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() session.end_session_and_clear_data() diff --git a/tabcmd/commands/auth/session.py b/tabcmd/commands/auth/session.py index ba824fca..41becac5 100644 --- a/tabcmd/commands/auth/session.py +++ b/tabcmd/commands/auth/session.py @@ -199,6 +199,9 @@ def _open_connection_with_opts(self) -> TSC.Server: return tableau_server def _verify_server_connection_unauthed(self): + if not self.tableau_server: + Errors.exit_with_error(self.logger, "Attempted to verify non-existent server connection") + return # make typing recognize self.tableau_server is not None after this line try: self.tableau_server.use_server_version() except requests.exceptions.ReadTimeout as timeout_error: @@ -259,10 +262,15 @@ def _validate_existing_signin(self): # server connection created, not yet logged in def _sign_in(self, tableau_auth) -> TSC.Server: - self.logger.debug(_("session.login") + self.server_url) - self.logger.debug(_("listsites.output").format("", self.username or self.token_name, self.site_name)) + self.logger.debug(_("session.login")) + self.logger.info(_("dataconnections.classes.tableau_server_site") + ": {}".format(self._site_display_name())) + # self.logger.debug(_("listsites.output").format("", self.username or self.token_name, self.site_name)) + if not self.tableau_server: + Errors.exit_with_error(self.logger, "Attempted to sign in with no server connection created") + return # make typing recognize self.tableau_server is not None after this line try: - self.tableau_server.auth.sign_in(tableau_auth) # it's the same call for token or user-pass + # it's the same call for token or user-pass + self.tableau_server.auth.sign_in(tableau_auth) except Exception as e: Errors.exit_with_error(self.logger, exception=e) try: @@ -296,7 +304,7 @@ def create_session(self, args, logger): self._read_existing_state() self._update_session_data(args) self.logging_level = args.logging_level or self.logging_level - self.logger = logger or log(__class__.__name__, self.logging_level) + self.logger = logger or log(self.__class__.__name__, self.logging_level) credentials = None if args.password or args.password_file: @@ -358,8 +366,8 @@ def _clear_data(self): self.tableau_server = None self.certificate = None - self.no_certcheck = None - self.no_proxy = None + self.no_certcheck = False + self.no_proxy = True self.proxy = None self.timeout = None @@ -444,7 +452,7 @@ def _save_file(self, data): json.dump(data, f) def _serialize_for_save(self): - data = {"tableau_auth": []} + data: Any = {"tableau_auth": []} data["tableau_auth"].append( { "auth_token": self.auth_token, diff --git a/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py b/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py index fb0d5daf..30ea4776 100644 --- a/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py +++ b/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py @@ -12,9 +12,6 @@ class DatasourcesAndWorkbooks(Server): Base Class for Operations related to Datasources and Workbooks """ - def __init__(self, args): - super().__init__(args) - @staticmethod def get_view_url_from_names(wb_name, view_name): return "{}/sheets/{}".format(wb_name, view_name) diff --git a/tabcmd/commands/datasources_and_workbooks/delete_command.py b/tabcmd/commands/datasources_and_workbooks/delete_command.py index 9ac1a9e3..eaf215fa 100644 --- a/tabcmd/commands/datasources_and_workbooks/delete_command.py +++ b/tabcmd/commands/datasources_and_workbooks/delete_command.py @@ -27,9 +27,9 @@ def define_args(delete_parser): set_project_r_arg(group) set_parent_project_arg(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -74,4 +74,4 @@ def run_command(args): server.datasources.delete(item_to_delete.id) logger.info(_("common.output.succeeded")) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/datasources_and_workbooks/export_command.py b/tabcmd/commands/datasources_and_workbooks/export_command.py index 23012b51..cfecb387 100644 --- a/tabcmd/commands/datasources_and_workbooks/export_command.py +++ b/tabcmd/commands/datasources_and_workbooks/export_command.py @@ -70,9 +70,9 @@ def define_args(export_parser): it to a file. This command can also export just the data used for a view_name """ - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -81,7 +81,7 @@ def run_command(args): if not view_content_url and not wb_content_url: view_example = "/workbook_name/view_name" message = "{} [{}]".format( - _("export.errors.requires_workbook_view_param").format(__class__.__name__), view_example + _("export.errors.requires_workbook_view_param").format(cls.__name__), view_example ) Errors.exit_with_error(logger, message) diff --git a/tabcmd/commands/datasources_and_workbooks/get_url_command.py b/tabcmd/commands/datasources_and_workbooks/get_url_command.py index 8eae77bc..a20eec72 100644 --- a/tabcmd/commands/datasources_and_workbooks/get_url_command.py +++ b/tabcmd/commands/datasources_and_workbooks/get_url_command.py @@ -32,14 +32,14 @@ def define_args(get_url_parser): # tabcmd get "/views/Finance/InvestmentGrowth.png?:size=640,480" -f growth.png # tabcmd get "/views/Finance/InvestmentGrowth.png?:refresh=yes" -f growth.png - @staticmethod - def run_command(args): + @classmethod + def run_command(cls, args): # A view can be returned in PDF, PNG, or CSV (summary data only) format. # A Tableau workbook is returned as a TWB if it connects to a datasource/live connection, # or a TWBX if it uses an extract. # A Tableau datasource is returned as a TDS if it connects to a live connection, # or a TDSX if it uses an extract. - logger = log(__class__.__name__, args.logging_level) + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index 3b567d52..9ee4698e 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -33,9 +33,9 @@ def define_args(publish_parser): set_append_replace_option(group) set_parent_project_arg(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/datasources_and_workbooks/runschedule_command.py b/tabcmd/commands/datasources_and_workbooks/runschedule_command.py index 9634c4e2..b874f9bc 100644 --- a/tabcmd/commands/datasources_and_workbooks/runschedule_command.py +++ b/tabcmd/commands/datasources_and_workbooks/runschedule_command.py @@ -18,9 +18,9 @@ def define_args(runschedule_parser): group = runschedule_parser.add_argument_group(title=RunSchedule.name) group.add_argument("schedule", help=_("tabcmd.run_schedule.options.schedule")) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/extracts/create_extracts_command.py b/tabcmd/commands/extracts/create_extracts_command.py index 3f39f04a..a0c2baae 100644 --- a/tabcmd/commands/extracts/create_extracts_command.py +++ b/tabcmd/commands/extracts/create_extracts_command.py @@ -26,9 +26,9 @@ def define_args(create_extract_parser): set_project_arg(group) set_parent_project_arg(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -39,9 +39,10 @@ def run_command(args): ) try: item = Extracts.get_wb_or_ds_for_extracts(args, logger, server) + job: TSC.JobItem if args.datasource: logger.info(_("createextracts.for.datasource").format(args.datasource)) - job: TSC.JobItem = server.datasources.create_extract(item, encrypt=args.encrypt) + job = server.datasources.create_extract(item, encrypt=args.encrypt) else: if not args.include_all and not args.embedded_datasources: @@ -51,7 +52,7 @@ def run_command(args): ) logger.info(_("createextracts.for.workbook_name").format(args.workbook)) - job: TSC.JobItem = server.workbooks.create_extract( + job = server.workbooks.create_extract( item, encrypt=args.encrypt, includeAll=args.include_all, @@ -62,7 +63,7 @@ def run_command(args): if args.continue_if_exists and Errors.is_resource_conflict(e): logger.info(_("tabcmd.result.already_exists").format(_("content_type.extract"), args.name)) return - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) logger.info(_("common.output.job_queued_success")) logger.debug("Extract creation queued with JobID: {}".format(job.id)) diff --git a/tabcmd/commands/extracts/decrypt_extracts_command.py b/tabcmd/commands/extracts/decrypt_extracts_command.py index cb47d7fa..8f7eae99 100644 --- a/tabcmd/commands/extracts/decrypt_extracts_command.py +++ b/tabcmd/commands/extracts/decrypt_extracts_command.py @@ -19,9 +19,9 @@ def define_args(decrypt_extract_parser): group = decrypt_extract_parser.add_argument_group(title=DecryptExtracts.name) group.add_argument("site_name", metavar="site-name", help=_("editsite.options.site-name")) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -30,7 +30,7 @@ def run_command(args): logger.info(_("decryptextracts.status").format(args.site_name)) job = server.sites.decrypt_extracts(site_item.id) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) logger.info(_("common.output.job_queued_success")) logger.debug("Extract decryption queued with JobID: {}".format(job.id)) diff --git a/tabcmd/commands/extracts/delete_extracts_command.py b/tabcmd/commands/extracts/delete_extracts_command.py index ab32a458..e9b2981f 100644 --- a/tabcmd/commands/extracts/delete_extracts_command.py +++ b/tabcmd/commands/extracts/delete_extracts_command.py @@ -25,17 +25,18 @@ def define_args(delete_extract_parser): set_project_arg(group) set_parent_project_arg(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) try: item = Extracts.get_wb_or_ds_for_extracts(args, logger, server) + job: TSC.JobItem if args.datasource: logger.info(_("deleteextracts.for.datasource").format(args.datasource)) - job: TSC.JobItem = server.datasources.delete_extract(item) + job = server.datasources.delete_extract(item) else: if not args.include_all and not args.embedded_datasources: Errors.exit_with_error( @@ -43,12 +44,12 @@ def run_command(args): _("extracts.workbook.errors.requires_datasources_or_include_all").format("deleteextracts"), ) logger.info(_("deleteextracts.for.workbook_name").format(args.workbook)) - job: TSC.JobItem = server.workbooks.delete_extract( + job = server.workbooks.delete_extract( item, includeAll=args.include_all, datasources=args.embedded_datasources ) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) logger.info(_("common.output.job_queued_success")) logger.debug("Extract deletion queued with JobID: {}".format(job.id)) diff --git a/tabcmd/commands/extracts/encrypt_extracts_command.py b/tabcmd/commands/extracts/encrypt_extracts_command.py index 0454d0ea..42d70cbd 100644 --- a/tabcmd/commands/extracts/encrypt_extracts_command.py +++ b/tabcmd/commands/extracts/encrypt_extracts_command.py @@ -21,9 +21,9 @@ def define_args(encrypt_extract_parser): group = encrypt_extract_parser.add_argument_group(title=EncryptExtracts.name) group.add_argument("site_name", metavar="site-name", help=_("editsite.options.site-name")) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -32,7 +32,7 @@ def run_command(args): logger.info(_("encryptextracts.status").format(site_item.name)) job = server.sites.encrypt_extracts(site_item.id) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) logger.info(_("common.output.job_queued_success")) logger.debug("Extract encryption queued with JobID: {}".format(job.id)) diff --git a/tabcmd/commands/extracts/extracts.py b/tabcmd/commands/extracts/extracts.py index 60c63c00..50729dbc 100644 --- a/tabcmd/commands/extracts/extracts.py +++ b/tabcmd/commands/extracts/extracts.py @@ -18,12 +18,11 @@ def get_wb_or_ds_for_extracts(args, logger, server): return datasource elif args.workbook or args.url: + workbook_item: TSC.WorkbookItem if args.workbook: - workbook_item: TSC.WorkbookItem = Server.get_workbook_item(logger, server, args.workbook, container) + workbook_item = Server.get_workbook_item(logger, server, args.workbook, container) else: - workbook_item: TSC.WorkbookItem = DatasourcesAndWorkbooks.get_wb_by_content_url( - logger, server, args.url - ) + workbook_item = DatasourcesAndWorkbooks.get_wb_by_content_url(logger, server, args.url) logger.info(_("export.status").format(workbook_item.name)) return workbook_item diff --git a/tabcmd/commands/extracts/reencrypt_extracts_command.py b/tabcmd/commands/extracts/reencrypt_extracts_command.py index e8eb363a..3026fff9 100644 --- a/tabcmd/commands/extracts/reencrypt_extracts_command.py +++ b/tabcmd/commands/extracts/reencrypt_extracts_command.py @@ -21,9 +21,9 @@ def define_args(reencrypt_extract_parser): group = reencrypt_extract_parser.add_argument_group(title=ReencryptExtracts.name) group.add_argument("site_name", metavar="site-name", help=_("editsite.options.site-name")) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -32,7 +32,7 @@ def run_command(args): logger.info(_("reencryptextracts.status").format(site_item.name)) job = server.sites.encrypt_extracts(site_item.id) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) logger.info(_("common.output.job_queued_success")) logger.debug("Extract re-encryption queued with JobID: {}".format(job.id)) diff --git a/tabcmd/commands/extracts/refresh_extracts_command.py b/tabcmd/commands/extracts/refresh_extracts_command.py index b88f220b..4d8aadc2 100644 --- a/tabcmd/commands/extracts/refresh_extracts_command.py +++ b/tabcmd/commands/extracts/refresh_extracts_command.py @@ -23,9 +23,9 @@ def define_args(refresh_extract_parser): set_project_arg(group) set_parent_project_arg(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -42,11 +42,12 @@ def run_command(args): try: item = Extracts.get_wb_or_ds_for_extracts(args, logger, server) + job: TSC.JobItem if args.datasource: logger.info(_("refreshextracts.status_refreshed").format(_("content_type.datasource"), args.datasource)) - job: TSC.JobItem = server.datasources.refresh(item.id) + job = server.datasources.refresh(item.id) else: - job: TSC.JobItem = server.workbooks.refresh(item.id) + job = server.workbooks.refresh(item.id) logger.info(_("refreshextracts.status_refreshed").format(_("content_type.workbook"), args.workbook)) except Exception as e: @@ -64,4 +65,4 @@ def run_command(args): logger.info("Job completed: ") logger.info(job_done) except Exception as je: - Errors.exit_with_error(logger, je) + Errors.exit_with_error(logger, exception=je) diff --git a/tabcmd/commands/group/create_group_command.py b/tabcmd/commands/group/create_group_command.py index 85efcaae..36950824 100644 --- a/tabcmd/commands/group/create_group_command.py +++ b/tabcmd/commands/group/create_group_command.py @@ -20,9 +20,9 @@ def define_args(create_group_parser): args_group = create_group_parser.add_argument_group(title=CreateGroupCommand.name) args_group.add_argument("name") - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/group/delete_group_command.py b/tabcmd/commands/group/delete_group_command.py index 71af42a5..5937c08c 100644 --- a/tabcmd/commands/group/delete_group_command.py +++ b/tabcmd/commands/group/delete_group_command.py @@ -20,9 +20,9 @@ def define_args(delete_group_parser): args_group = delete_group_parser.add_argument_group(title=DeleteGroupCommand.name) args_group.add_argument("name") - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/project/create_project_command.py b/tabcmd/commands/project/create_project_command.py index 85853edd..5b8c4674 100644 --- a/tabcmd/commands/project/create_project_command.py +++ b/tabcmd/commands/project/create_project_command.py @@ -25,9 +25,9 @@ def define_args(create_project_parser): set_parent_project_arg(create_project_parser) set_description_arg(create_project_parser) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -37,8 +37,8 @@ def run_command(args): try: logger.info(_("tabcmd.find.parent_project").format(args.parent_project_path)) parent = Server.get_project_by_name_and_parent_path(logger, server, None, args.parent_project_path) - except Exception as exc: - Errors.exit_with_error(logger, exc) + except Exception as e: + Errors.exit_with_error(logger, exception=e) readable_name = "{0}/{1}".format(args.parent_project_path, args.project_name) parent_id = parent.id logger.debug("parent project = `{0}`, id = {1}".format(args.parent_project_path, parent_id)) @@ -54,6 +54,6 @@ def run_command(args): logger.info(_("tabcmd.result.already_exists").format(_("content_type.project"), args.project_name)) logger.info(_("common.output.succeeded")) else: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) return project_item diff --git a/tabcmd/commands/project/delete_project_command.py b/tabcmd/commands/project/delete_project_command.py index b107406c..422e504e 100644 --- a/tabcmd/commands/project/delete_project_command.py +++ b/tabcmd/commands/project/delete_project_command.py @@ -22,9 +22,9 @@ def define_args(delete_project_parser): args_group.add_argument("project_name", metavar="project-name", help=_("createproject.options.name")) set_parent_project_arg(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -37,7 +37,7 @@ def run_command(args): logger, server, args.project_name, args.parent_project_path ) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) project_id = project.id try: diff --git a/tabcmd/commands/project/publish_samples_command.py b/tabcmd/commands/project/publish_samples_command.py index 046e4491..d46f305a 100644 --- a/tabcmd/commands/project/publish_samples_command.py +++ b/tabcmd/commands/project/publish_samples_command.py @@ -26,9 +26,9 @@ def define_args(publish_samples_parser): ) set_parent_project_arg(args_group) # args.parent_project_name - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/server.py b/tabcmd/commands/server.py index d315e6dd..b67b4ad2 100644 --- a/tabcmd/commands/server.py +++ b/tabcmd/commands/server.py @@ -113,7 +113,9 @@ def get_filename_extension_if_tableau_type(logger, filename): ) @staticmethod - def get_project_by_name_and_parent_path(logger, server, project_name: str, parent_path: str) -> TSC.ProjectItem: + def get_project_by_name_and_parent_path( + logger, server, project_name: Optional[str], parent_path: Optional[str] + ) -> TSC.ProjectItem: logger.debug(_("content_type.project") + ":{0}, {1}".format(parent_path, project_name)) if not parent_path: if not project_name: @@ -135,7 +137,7 @@ def get_project_by_name_and_parent_path(logger, server, project_name: str, paren return project @staticmethod - def _parse_project_path_to_list(project_path: str): + def _parse_project_path_to_list(project_path: Optional[str]): if project_path is None or project_path == "": return [] if project_path.find("/") == -1: diff --git a/tabcmd/commands/site/create_site_command.py b/tabcmd/commands/site/create_site_command.py index f684ba1f..947534d9 100644 --- a/tabcmd/commands/site/create_site_command.py +++ b/tabcmd/commands/site/create_site_command.py @@ -22,9 +22,9 @@ def define_args(create_site_parser): args_group.add_argument("new_site_name", metavar="site-name", help=_("editsite.options.site-name")) set_common_site_args(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -46,4 +46,4 @@ def run_command(args): if Errors.is_resource_conflict(e) and args.continue_if_exists: logger.info(_("createsite.errors.site_name_already_exists").format(args.new_site_name)) return - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/site/delete_site_command.py b/tabcmd/commands/site/delete_site_command.py index f1892730..a6bbf851 100644 --- a/tabcmd/commands/site/delete_site_command.py +++ b/tabcmd/commands/site/delete_site_command.py @@ -20,9 +20,9 @@ def define_args(delete_site_parser): args_group = delete_site_parser.add_argument_group(title=DeleteSiteCommand.name) args_group.add_argument("site_name_to_delete", metavar="site-name", help=strings[2]) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/site/edit_site_command.py b/tabcmd/commands/site/edit_site_command.py index ab6451ed..8b92a8ae 100644 --- a/tabcmd/commands/site/edit_site_command.py +++ b/tabcmd/commands/site/edit_site_command.py @@ -25,9 +25,9 @@ def define_args(edit_site_parser): set_common_site_args(args_group) set_site_status_arg(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -47,4 +47,4 @@ def run_command(args): logger.info(_("common.output.succeeded")) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/site/list_command.py b/tabcmd/commands/site/list_command.py index 64e4fa48..b33be9c1 100644 --- a/tabcmd/commands/site/list_command.py +++ b/tabcmd/commands/site/list_command.py @@ -29,8 +29,8 @@ def define_args(list_parser): ) args_group.add_argument("-d", "--details", action="store_true", help="Show object details") - @staticmethod - def run_command(args): + @classmethod + def run_command(cls, args): logger = log(__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() @@ -63,4 +63,4 @@ def run_command(args): logger.info(ListCommand.local_strings["tabcmd_listing_label_name"].format(item.name)) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/site/list_sites_command.py b/tabcmd/commands/site/list_sites_command.py index f124fbe6..f3612c46 100644 --- a/tabcmd/commands/site/list_sites_command.py +++ b/tabcmd/commands/site/list_sites_command.py @@ -21,9 +21,9 @@ def define_args(list_site_parser): group = list_site_parser.add_argument_group(title=ListSiteCommand.name) set_site_detail_option(group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) @@ -36,4 +36,4 @@ def run_command(args): if args.get_extract_encryption_mode: logger.info("EXTRACTENCRYPTION:", site.extract_encryption_mode) except Exception as e: - Errors.exit_with_error(logger, e) + Errors.exit_with_error(logger, exception=e) diff --git a/tabcmd/commands/user/add_users_command.py b/tabcmd/commands/user/add_users_command.py index 1fc79b46..8290d0b4 100644 --- a/tabcmd/commands/user/add_users_command.py +++ b/tabcmd/commands/user/add_users_command.py @@ -20,9 +20,9 @@ def define_args(add_user_parser): set_users_file_arg(args_group) set_completeness_options(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/user/create_site_users.py b/tabcmd/commands/user/create_site_users.py index ce515fec..0382d33d 100644 --- a/tabcmd/commands/user/create_site_users.py +++ b/tabcmd/commands/user/create_site_users.py @@ -26,9 +26,9 @@ def define_args(create_site_users_parser): set_completeness_options(args_group) UserCommand.set_auth_arg(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/user/create_users_command.py b/tabcmd/commands/user/create_users_command.py index 1c3ab4fa..317190a7 100644 --- a/tabcmd/commands/user/create_users_command.py +++ b/tabcmd/commands/user/create_users_command.py @@ -26,9 +26,9 @@ def define_args(create_users_parser): set_completeness_options(args_group) UserCommand.set_auth_arg(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/user/delete_site_users_command.py b/tabcmd/commands/user/delete_site_users_command.py index 731ed7bf..bc6fcc91 100644 --- a/tabcmd/commands/user/delete_site_users_command.py +++ b/tabcmd/commands/user/delete_site_users_command.py @@ -23,9 +23,9 @@ def define_args(delete_site_users_parser): set_users_file_positional(args_group) set_completeness_options(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/commands/user/remove_users_command.py b/tabcmd/commands/user/remove_users_command.py index 38f0f720..9652d9b4 100644 --- a/tabcmd/commands/user/remove_users_command.py +++ b/tabcmd/commands/user/remove_users_command.py @@ -20,9 +20,9 @@ def define_args(remove_users_parser): set_users_file_arg(args_group) set_completeness_options(args_group) - @staticmethod - def run_command(args): - logger = log(__class__.__name__, args.logging_level) + @classmethod + def run_command(cls, args): + logger = log(cls.__name__, args.logging_level) logger.debug(_("tabcmd.launching")) session = Session() server = session.create_session(args, logger) diff --git a/tabcmd/execution/logger_config.py b/tabcmd/execution/logger_config.py index 17fe6907..f83f2b59 100644 --- a/tabcmd/execution/logger_config.py +++ b/tabcmd/execution/logger_config.py @@ -41,7 +41,7 @@ def add_trace_level(): FORMATS[trace_level] = FORMATS[logging.ERROR] -def configure_log(name: str, logging_level_input: str): +def configure_log(name: str, logging_level_input: str) -> logging.Logger: """function for logging statements to console and logfile""" logging_level = getattr(logging, logging_level_input.upper()) log_format = FORMATS[logging_level] @@ -61,5 +61,5 @@ def configure_log(name: str, logging_level_input: str): def log(file_name, logging_level): logger = configure_log(file_name, logging_level) if not hasattr(logger, "trace"): - logger.trace = logger.debug + logger.trace = logger.debug # type: ignore return logger diff --git a/tabcmd/execution/parent_parser.py b/tabcmd/execution/parent_parser.py index bc81f59c..4332abec 100644 --- a/tabcmd/execution/parent_parser.py +++ b/tabcmd/execution/parent_parser.py @@ -190,7 +190,7 @@ def __init__(self, _parser: ParentParser): def run_command(self, args): logger = log(__name__, "info") logger.info(strings[6] + " " + version + "\n") - logger.info(self.parser.root.format_help()) + logger.info(self.parser.root.format_help()) # type: ignore strings = [ diff --git a/tabcmd/tabcmd.py b/tabcmd/tabcmd.py index 70b8b63d..ec0fe00a 100644 --- a/tabcmd/tabcmd.py +++ b/tabcmd/tabcmd.py @@ -17,13 +17,14 @@ def main(): print("Keyboard Interrupt: exiting") sys.exit(1) except Exception as e: - sys.stderr.writelines( - [ - "ERROR\n", - "Unhandled exception: {}\n".format(type(e).__name__), - f"at line {e.__traceback__.tb_lineno} of {__file__}: {e}\n", - ] - ) + if e.__traceback__: + sys.stderr.writelines( + [ + "ERROR\n", + "Unhandled exception: {}\n".format(type(e).__name__), + f"at line {e.__traceback__.tb_lineno} of {__file__}: {e}\n", + ] + ) sys.exit(1) diff --git a/tests/commands/test_session.py b/tests/commands/test_session.py index cb60260d..4d2118ff 100644 --- a/tests/commands/test_session.py +++ b/tests/commands/test_session.py @@ -156,7 +156,7 @@ def test__create_new_username_credential_succeeds_new_password(self, mock_pass): test_password = "pword1" active_session = Session() active_session.username = "user" - active_session.site = "" + active_session.site_name = "" auth = active_session._create_new_credential(test_password, Session.PASSWORD_CRED_TYPE) assert auth is not None @@ -175,7 +175,7 @@ def test__create_new_token_credential_succeeds_from_self(self, mock_pass): def test__create_new_username_credential_succeeds_from_self(self, mock_pass): active_session = Session() active_session.username = "user3" - active_session.site = "" + active_session.site_name = "" auth = active_session._create_new_credential(None, Session.PASSWORD_CRED_TYPE) assert mock_pass.has_been_called() assert auth is not None @@ -201,18 +201,24 @@ def test__create_new_username_credential_succeeds_from_args(self, mock_pass): class PromptingTests(unittest.TestCase): def test_show_prompt_if_user_didnt_say(self): + session: Session = Session() test_args = Namespace(**vars(args_to_mock)) - assert Session._allow_prompt(test_args) is True, test_args + session._update_session_data(test_args) + assert session._allow_prompt() is True, test_args def test_show_prompt_if_user_said_yes(self): + session: Session = Session() test_args = Namespace(**vars(args_to_mock)) test_args.prompt = True - assert Session._allow_prompt(test_args) is True, test_args + session._update_session_data(test_args) + assert session._allow_prompt() is True, test_args def test_dont_show_prompt_if_user_said_no(self): + session: Session = Session() test_args = Namespace(**vars(args_to_mock)) test_args.no_prompt = True - assert Session._allow_prompt(test_args) is False, test_args + session._update_session_data(test_args) + assert session._allow_prompt() is False, test_args """ diff --git a/tests/e2e/language_tests.py b/tests/e2e/language_tests.py index 6fced7a3..9d45673d 100644 --- a/tests/e2e/language_tests.py +++ b/tests/e2e/language_tests.py @@ -101,6 +101,12 @@ def _get_datasource(self, server_file): arguments = [command, server_file] _test_command(arguments) + def _get_workbook(self, server_file): + command = "get" + server_file = "/workbooks/" + server_file + arguments = [command, server_file] + _test_command(arguments) + def _get_custom_view(self): command = "get" diff --git a/tests/e2e/tests_integration.py b/tests/e2e/tests_integration.py index f63588b1..72c26523 100644 --- a/tests/e2e/tests_integration.py +++ b/tests/e2e/tests_integration.py @@ -1,7 +1,9 @@ import argparse import logging import pytest +import tableauserverclient as TSC import unittest + from tabcmd.commands.auth.session import Session from tabcmd.commands.server import Server from tabcmd.execution.logger_config import log @@ -13,6 +15,7 @@ credentials = None # type: ignore fakeserver = "http://SRVR" +logger: logging.Logger logging.disable(logging.ERROR) # these are integration tests because they don't just run a command, they call interior methods @@ -130,9 +133,8 @@ def test_read_password_file(self): # test_session.create_session(args) def test_get_project(self): - logger = log(__class__.__name__, "info") server = E2EServerTests.test_log_in() - Server.get_project_by_name_and_parent_path(logger, server, "Default", None) + project: TSC.ProjectItem = Server.get_project_by_name_and_parent_path(logger, server, "Default", None) logging.disable(logging.NOTSET) diff --git a/tests/parsers/common_setup.py b/tests/parsers/common_setup.py index 619d4805..40c1a481 100644 --- a/tests/parsers/common_setup.py +++ b/tests/parsers/common_setup.py @@ -1,7 +1,9 @@ +import argparse +import unittest + from tabcmd.execution import parent_parser from collections import namedtuple - encoding = "utf-8-sig" @@ -13,7 +15,7 @@ def mock_command_action(): def initialize_test_pieces(commandname, command_object): manager = parent_parser.ParentParser() parser = manager.get_root_parser() - mock_command = namedtuple("TestObject", "name, run_command, description, define_args") + mock_command = namedtuple("mock_command", "name, run_command, description, define_args") mock_command.name = commandname mock_command.run_command = mock_command_action mock_command.description = "mock help text" @@ -31,3 +33,7 @@ def initialize_test_pieces(commandname, command_object): bad mix of optional arguments has unknown arguments """ + + +class ParserTestCase(unittest.TestCase): + parser_under_test: argparse.ArgumentParser diff --git a/tests/parsers/test_login_parser.py b/tests/parsers/test_login_parser.py index 2276ef45..23a055ba 100644 --- a/tests/parsers/test_login_parser.py +++ b/tests/parsers/test_login_parser.py @@ -8,7 +8,7 @@ @mock.patch("sys.argv", None) -class LoginParserTest(unittest.TestCase): +class LoginParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, LoginCommand) diff --git a/tests/parsers/test_logout_parser.py b/tests/parsers/test_logout_parser.py index 5d59eab7..09672d15 100644 --- a/tests/parsers/test_logout_parser.py +++ b/tests/parsers/test_logout_parser.py @@ -8,7 +8,7 @@ commandname = "logout" -class LogoutParserTest(unittest.TestCase): +class LogoutParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, LogoutCommand) diff --git a/tests/parsers/test_parser_add_user.py b/tests/parsers/test_parser_add_user.py index fe57f669..ad9ee455 100644 --- a/tests/parsers/test_parser_add_user.py +++ b/tests/parsers/test_parser_add_user.py @@ -7,7 +7,7 @@ commandname = "addusers" -class AddUsersParserTest(unittest.TestCase): +class AddUsersParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, AddUserCommand) @@ -26,7 +26,7 @@ def test_add_users_parser_users_file(self): with mock.patch("builtins.open", mock.mock_open(read_data="test")) as open_file: mock_args = [commandname, "group-name", "--users", "users.csv"] args = self.parser_under_test.parse_args(mock_args) - self.assertEqual(args.name, "group-name"), args + self.assertEqual(args.name, "group-name", args) open_file.assert_called_with("users.csv", "r", -1, encoding, None), args @mock.patch("builtins.open") diff --git a/tests/parsers/test_parser_create_extracts.py b/tests/parsers/test_parser_create_extracts.py index 8a6d3f49..38fddc4a 100644 --- a/tests/parsers/test_parser_create_extracts.py +++ b/tests/parsers/test_parser_create_extracts.py @@ -6,7 +6,7 @@ commandname = "createextracts" -class CreateExtractsParserTest(unittest.TestCase): +class CreateExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateExtracts) diff --git a/tests/parsers/test_parser_create_group.py b/tests/parsers/test_parser_create_group.py index de05934d..4b5b32e5 100644 --- a/tests/parsers/test_parser_create_group.py +++ b/tests/parsers/test_parser_create_group.py @@ -6,7 +6,7 @@ commandname = "creategroup" -class CreateGroupParserTest(unittest.TestCase): +class CreateGroupParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateGroupCommand) diff --git a/tests/parsers/test_parser_create_project.py b/tests/parsers/test_parser_create_project.py index b02319c6..3fb6a57f 100644 --- a/tests/parsers/test_parser_create_project.py +++ b/tests/parsers/test_parser_create_project.py @@ -6,7 +6,7 @@ commandname = "createproject" -class CreateProjectParserTest(unittest.TestCase): +class CreateProjectParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateProjectCommand) diff --git a/tests/parsers/test_parser_create_site.py b/tests/parsers/test_parser_create_site.py index ebe08813..bbf08fa7 100644 --- a/tests/parsers/test_parser_create_site.py +++ b/tests/parsers/test_parser_create_site.py @@ -6,7 +6,7 @@ commandname = "createsite" -class CreateSiteParserTest(unittest.TestCase): +class CreateSiteParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateSiteCommand) diff --git a/tests/parsers/test_parser_create_site_users.py b/tests/parsers/test_parser_create_site_users.py index 5b46bd00..e7f80a81 100644 --- a/tests/parsers/test_parser_create_site_users.py +++ b/tests/parsers/test_parser_create_site_users.py @@ -7,7 +7,7 @@ commandname = "createsiteusers" -class CreateSiteUsersParserTest(unittest.TestCase): +class CreateSiteUsersParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateSiteUsersCommand) diff --git a/tests/parsers/test_parser_create_user.py b/tests/parsers/test_parser_create_user.py index 7b67f24f..7240c0ce 100644 --- a/tests/parsers/test_parser_create_user.py +++ b/tests/parsers/test_parser_create_user.py @@ -7,7 +7,7 @@ commandname = "createusers" -class CreateUsersTest(unittest.TestCase): +class CreateUsersTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, CreateUsersCommand) diff --git a/tests/parsers/test_parser_decrypt_extracts.py b/tests/parsers/test_parser_decrypt_extracts.py index 1fdd5dc2..02f79e43 100644 --- a/tests/parsers/test_parser_decrypt_extracts.py +++ b/tests/parsers/test_parser_decrypt_extracts.py @@ -6,7 +6,7 @@ commandname = "decryptextracts" -class DecryptExtractsParserTest(unittest.TestCase): +class DecryptExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DecryptExtracts) diff --git a/tests/parsers/test_parser_delete.py b/tests/parsers/test_parser_delete.py index 9d6900d0..5747befb 100644 --- a/tests/parsers/test_parser_delete.py +++ b/tests/parsers/test_parser_delete.py @@ -6,7 +6,7 @@ commandname = "delete" -class DeleteParserTest(unittest.TestCase): +class DeleteParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DeleteCommand) diff --git a/tests/parsers/test_parser_delete_extracts.py b/tests/parsers/test_parser_delete_extracts.py index 1d71d187..f7e9e162 100644 --- a/tests/parsers/test_parser_delete_extracts.py +++ b/tests/parsers/test_parser_delete_extracts.py @@ -6,7 +6,7 @@ commandname = "deleteextracts" -class DeleteExtractsParserTest(unittest.TestCase): +class DeleteExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DeleteExtracts) diff --git a/tests/parsers/test_parser_delete_group.py b/tests/parsers/test_parser_delete_group.py index 368e3cce..887230da 100644 --- a/tests/parsers/test_parser_delete_group.py +++ b/tests/parsers/test_parser_delete_group.py @@ -6,7 +6,7 @@ commandname = "deletegroup" -class DeleteGroupParserTestT(unittest.TestCase): +class DeleteGroupParserTestT(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DeleteGroupCommand) diff --git a/tests/parsers/test_parser_delete_project.py b/tests/parsers/test_parser_delete_project.py index 5ecf43bc..5291b6c9 100644 --- a/tests/parsers/test_parser_delete_project.py +++ b/tests/parsers/test_parser_delete_project.py @@ -6,7 +6,7 @@ commandname = "deleteproject" -class DeleteProjectParserTest(unittest.TestCase): +class DeleteProjectParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DeleteProjectCommand) diff --git a/tests/parsers/test_parser_delete_site.py b/tests/parsers/test_parser_delete_site.py index 2d9562f7..7a82cd90 100644 --- a/tests/parsers/test_parser_delete_site.py +++ b/tests/parsers/test_parser_delete_site.py @@ -6,7 +6,7 @@ commandname = "deletesite" -class DeleteSiteParserTest(unittest.TestCase): +class DeleteSiteParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, DeleteSiteCommand) diff --git a/tests/parsers/test_parser_delete_site_user.py b/tests/parsers/test_parser_delete_site_user.py index c0d81b7e..c61103ff 100644 --- a/tests/parsers/test_parser_delete_site_user.py +++ b/tests/parsers/test_parser_delete_site_user.py @@ -7,7 +7,7 @@ commandname = "deletesiteusers" -class DeleteSiteUsersParserTest(unittest.TestCase): +class DeleteSiteUsersParserTest(ParserTestCase): csv = ("testname", "testpassword", "test", "test", "test", "test") @classmethod diff --git a/tests/parsers/test_parser_edit_site.py b/tests/parsers/test_parser_edit_site.py index 447deecf..d36223d5 100644 --- a/tests/parsers/test_parser_edit_site.py +++ b/tests/parsers/test_parser_edit_site.py @@ -6,7 +6,7 @@ commandname = "editsites" -class EditSiteParserTest(unittest.TestCase): +class EditSiteParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, EditSiteCommand) diff --git a/tests/parsers/test_parser_encrypt_extracts.py b/tests/parsers/test_parser_encrypt_extracts.py index e87f8f2a..7e170137 100644 --- a/tests/parsers/test_parser_encrypt_extracts.py +++ b/tests/parsers/test_parser_encrypt_extracts.py @@ -6,7 +6,7 @@ commandname = "encryptextracts" -class EncryptExtractsParserTest(unittest.TestCase): +class EncryptExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, EncryptExtracts) diff --git a/tests/parsers/test_parser_export.py b/tests/parsers/test_parser_export.py index f90999a9..542276f2 100644 --- a/tests/parsers/test_parser_export.py +++ b/tests/parsers/test_parser_export.py @@ -6,7 +6,7 @@ commandname = "export" -class ExportParserTest(unittest.TestCase): +class ExportParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, ExportCommand) diff --git a/tests/parsers/test_parser_get_url.py b/tests/parsers/test_parser_get_url.py index 5467e236..fa07c1fa 100644 --- a/tests/parsers/test_parser_get_url.py +++ b/tests/parsers/test_parser_get_url.py @@ -6,13 +6,13 @@ commandname = "listsites" -class GetUrlParserTest(unittest.TestCase): +class GetUrlParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, GetUrl) def test_get_url_parser_file(self): - mock_args = vars(argparse.Namespace(filename="helloworld")) + mock_args = list(vars(argparse.Namespace(filename="helloworld"))) with self.assertRaises(SystemExit): args = self.parser_under_test.parse_args(mock_args) diff --git a/tests/parsers/test_parser_list_sites.py b/tests/parsers/test_parser_list_sites.py index 71b01234..1c1e448f 100644 --- a/tests/parsers/test_parser_list_sites.py +++ b/tests/parsers/test_parser_list_sites.py @@ -6,7 +6,7 @@ commandname = "listsites" -class ListSitesParserTest(unittest.TestCase): +class ListSitesParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, ListSiteCommand) diff --git a/tests/parsers/test_parser_publish.py b/tests/parsers/test_parser_publish.py index 65aaa20d..e3fc263c 100644 --- a/tests/parsers/test_parser_publish.py +++ b/tests/parsers/test_parser_publish.py @@ -7,7 +7,7 @@ commandname = "Publish" -class PublishParserTest(unittest.TestCase): +class PublishParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, PublishCommand) diff --git a/tests/parsers/test_parser_publish_samples.py b/tests/parsers/test_parser_publish_samples.py index 43c4062a..36fef9f2 100644 --- a/tests/parsers/test_parser_publish_samples.py +++ b/tests/parsers/test_parser_publish_samples.py @@ -6,7 +6,7 @@ commandname = "publishsamples" -class PublishSamplesParserTest(unittest.TestCase): +class PublishSamplesParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, PublishSamplesCommand) diff --git a/tests/parsers/test_parser_reencrypt_extracts.py b/tests/parsers/test_parser_reencrypt_extracts.py index 79d82cda..e919ea70 100644 --- a/tests/parsers/test_parser_reencrypt_extracts.py +++ b/tests/parsers/test_parser_reencrypt_extracts.py @@ -6,7 +6,7 @@ commandname = "reencryptextracts" -class ReencryptExtractsParserTest(unittest.TestCase): +class ReencryptExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, ReencryptExtracts) diff --git a/tests/parsers/test_parser_refresh_extracts.py b/tests/parsers/test_parser_refresh_extracts.py index a74ac760..10d5e5e8 100644 --- a/tests/parsers/test_parser_refresh_extracts.py +++ b/tests/parsers/test_parser_refresh_extracts.py @@ -6,7 +6,7 @@ commandname = "refreshextracts" -class RefreshExtractsParserTest(unittest.TestCase): +class RefreshExtractsParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, RefreshExtracts) diff --git a/tests/parsers/test_parser_remove_user.py b/tests/parsers/test_parser_remove_user.py index 6441354a..e5650af8 100644 --- a/tests/parsers/test_parser_remove_user.py +++ b/tests/parsers/test_parser_remove_user.py @@ -7,7 +7,7 @@ commandname = "removeusers" -class RemoveUsersParserTest(unittest.TestCase): +class RemoveUsersParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, RemoveUserCommand) diff --git a/tests/parsers/test_parser_runschedule.py b/tests/parsers/test_parser_runschedule.py index 445dfb58..5d6a174d 100644 --- a/tests/parsers/test_parser_runschedule.py +++ b/tests/parsers/test_parser_runschedule.py @@ -6,7 +6,7 @@ commandname = "runschedule" -class RunScheduleParserTest(unittest.TestCase): +class RunScheduleParserTest(ParserTestCase): @classmethod def setUpClass(cls): cls.parser_under_test = initialize_test_pieces(commandname, RunSchedule) From f6254738d678753233250795de13c44f4dae315f Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 13 Jun 2024 23:19:21 -0700 Subject: [PATCH 09/10] fix several files --- tabcmd/commands/auth/session.py | 45 ++++++++++--------- .../publish_command.py | 7 +-- tests/commands/test_user_utils.py | 6 ++- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/tabcmd/commands/auth/session.py b/tabcmd/commands/auth/session.py index 41becac5..b9e530fb 100644 --- a/tabcmd/commands/auth/session.py +++ b/tabcmd/commands/auth/session.py @@ -12,7 +12,8 @@ from tabcmd.execution.localize import _ from tabcmd.execution.logger_config import log -from typing import Dict, Any +# 3.10 introduced X | None to replace Optional. We are still using Optional. +from typing import Dict, Any, Optional class Session: @@ -51,7 +52,7 @@ def __init__(self): self.logging_level = "info" self.logger = log(__name__, self.logging_level) # instantiate here mostly for tests self._read_from_json() - self.tableau_server = None # this one is an object that doesn't get persisted in the file + self.tableau_server: Optional[TSC.Server] = None # this one is an object that doesn't get persisted in the file # called before we connect to the server # generally, we don't want to overwrite stored data with nulls @@ -198,10 +199,11 @@ def _open_connection_with_opts(self) -> TSC.Server: self.logger.debug("Finished setting up connection") return tableau_server - def _verify_server_connection_unauthed(self): + # TODO pass in server object + def _verify_server_connection_unauthed(self) -> None: if not self.tableau_server: Errors.exit_with_error(self.logger, "Attempted to verify non-existent server connection") - return # make typing recognize self.tableau_server is not None after this line + return # make typing recognize self.tableau_server is not None after this line try: self.tableau_server.use_server_version() except requests.exceptions.ReadTimeout as timeout_error: @@ -217,7 +219,7 @@ def _verify_server_connection_unauthed(self): except Exception as e: Errors.exit_with_error(self.logger, exception=e) - def _create_new_connection(self) -> TSC.Server: + def _create_new_connection(self) -> Optional[TSC.Server]: self._print_server_info() self.logger.info(_("session.connecting")) try: @@ -226,11 +228,11 @@ def _create_new_connection(self) -> TSC.Server: Errors.exit_with_error(self.logger, "Failed to connect to server", e) return self.tableau_server - def _read_existing_state(self): + def _read_existing_state(self) -> None: if self._json_exists(): self._read_from_json() - def _print_server_info(self): + def _print_server_info(self) -> None: self.logger.info("===== Server: {}".format(self.server_url)) if self.proxy: self.logger.info("===== Proxy: {}".format(self.proxy)) @@ -244,7 +246,7 @@ def _print_server_info(self): self.logger.info(_("dataconnections.classes.tableau_server_site") + ": {}".format(site_display_name)) # side-effect: sets self.username - def _validate_existing_signin(self): + def _validate_existing_signin(self) -> Optional[TSC.Server]: # when do these two messages show up? self.logger.info(_("session.auto_site_login")) try: if self.tableau_server and self.tableau_server.is_signed_in(): @@ -260,32 +262,35 @@ def _validate_existing_signin(self): self.logger.info(_("errors.internal_error.request.message"), e) return None - # server connection created, not yet logged in + # server connection must be created, not yet logged in + # TODO: pass in the server instance to auth? def _sign_in(self, tableau_auth) -> TSC.Server: self.logger.debug(_("session.login")) - self.logger.info(_("dataconnections.classes.tableau_server_site") + ": {}".format(self._site_display_name())) + self.logger.info(_("dataconnections.classes.tableau_server_site") + ": {}".format(self.site_name)) # self.logger.debug(_("listsites.output").format("", self.username or self.token_name, self.site_name)) - if not self.tableau_server: + # must explicitly check 'is None' to treat the Optional as Not None + if self.tableau_server is not None: + connected_server: TSC.Server = self.tableau_server + else: Errors.exit_with_error(self.logger, "Attempted to sign in with no server connection created") - return # make typing recognize self.tableau_server is not None after this line + try: - # it's the same call for token or user-pass - self.tableau_server.auth.sign_in(tableau_auth) + connected_server.auth.sign_in(tableau_auth) except Exception as e: Errors.exit_with_error(self.logger, exception=e) try: - self.site_id = self.tableau_server.site_id - self.user_id = self.tableau_server.user_id - self.auth_token = self.tableau_server._auth_token - success = self._validate_existing_signin() + self.site_id = connected_server.site_id + self.user_id = connected_server.user_id + self.auth_token = connected_server._auth_token + signed_in_object = self._validate_existing_signin() except Exception as e: Errors.exit_with_error(self.logger, exception=e) - if success: + if signed_in_object: self.logger.info(_("common.output.succeeded")) else: Errors.exit_with_error(self.logger, message="Sign in failed") + return connected_server - return self.tableau_server def _get_saved_credentials(self): if self.last_login_using == "username": diff --git a/tabcmd/commands/datasources_and_workbooks/publish_command.py b/tabcmd/commands/datasources_and_workbooks/publish_command.py index f5799e56..b3c6861f 100644 --- a/tabcmd/commands/datasources_and_workbooks/publish_command.py +++ b/tabcmd/commands/datasources_and_workbooks/publish_command.py @@ -1,3 +1,4 @@ +from typing import Optional import tableauserverclient as TSC from tableauserverclient import ServerResponseError @@ -105,7 +106,7 @@ def run_command(cls, args): def get_publish_mode(args, logger): # default: fail if it already exists on the server default_mode = TSC.Server.PublishMode.CreateNew - publish_mode = default_mode + publish_mode : Optional[str] = default_mode if args.replace: raise AttributeError("Replacing an extract is not yet implemented") @@ -124,7 +125,7 @@ def get_publish_mode(args, logger): # Overwrites the workbook, data source, or data extract if it already exists on the server. publish_mode = TSC.Server.PublishMode.Overwrite - if not publish_mode: + if publish_mode is None: Errors.exit_with_error(logger, "Invalid combination of publishing options (Append, Overwrite, Replace)") - logger.debug("Publish mode selected: " + publish_mode) + logger.debug("Publish mode selected: " + str(publish_mode)) return publish_mode diff --git a/tests/commands/test_user_utils.py b/tests/commands/test_user_utils.py index b22f7ff1..cb49e0d0 100644 --- a/tests/commands/test_user_utils.py +++ b/tests/commands/test_user_utils.py @@ -73,7 +73,8 @@ def test_get_user_detail_empty_line(self): def test_get_user_detail_standard(self): test_line = "username, pword, fname, license, admin, pub, email" - test_user: TSC.UserItem = UserCommand._parse_line(test_line) + test_user: TSC.UserItem | None = UserCommand._parse_line(test_line) + assert test_user is not None assert test_user.name == "username", test_user.name assert test_user.fullname == "fname", test_user.fullname assert test_user.site_role == "Unlicensed", test_user.site_role @@ -81,7 +82,8 @@ def test_get_user_detail_standard(self): def test_get_user_details_only_username(self): test_line = "username" - test_user: TSC.UserItem = UserCommand._parse_line(test_line) + test_user: TSC.UserItem | None = UserCommand._parse_line(test_line) + assert test_user is not None def test_populate_user_details_only_some(self): values = ["username", "", "", "creator", "admin"] From 3ce2471211c06c61e78863f690e5355eb7db1bea Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Thu, 13 Jun 2024 23:24:33 -0700 Subject: [PATCH 10/10] create union type for RequestOptions --- .../datasources_and_workbooks_command.py | 12 ++++++++---- .../datasources_and_workbooks/export_command.py | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py b/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py index 30ea4776..52ad76ba 100644 --- a/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py +++ b/tabcmd/commands/datasources_and_workbooks/datasources_and_workbooks_command.py @@ -1,3 +1,4 @@ +from typing import Union import urllib import tableauserverclient as TSC @@ -6,6 +7,9 @@ from tabcmd.commands.server import Server from tabcmd.execution.localize import _ +# TODO: expose a base type for these +RequestOptions = Union[TSC.PDFRequestOptions, TSC.CSVRequestOptions, TSC.ImageRequestOptions] + class DatasourcesAndWorkbooks(Server): """ @@ -59,7 +63,7 @@ def get_ds_by_content_url(logger, server, datasource_content_url) -> TSC.Datasou return matching_datasources[0] @staticmethod - def apply_values_from_url_params(logger, request_options: TSC.PDFRequestOptions, url) -> None: + def apply_values_from_url_params(logger, request_options: RequestOptions, url: str) -> None: logger.debug(url) try: if "?" in url: @@ -83,7 +87,7 @@ def apply_values_from_url_params(logger, request_options: TSC.PDFRequestOptions, # this is called from within from_url_params, for each view_filter value @staticmethod - def apply_encoded_filter_value(logger, request_options, value): + def apply_encoded_filter_value(logger, request_options: RequestOptions, value: str) -> None: # the REST API doesn't appear to have the option to disambiguate with "Parameters." value = value.replace("Parameters.", "") # the filter values received from the url are already url encoded. tsc will encode them again. @@ -96,14 +100,14 @@ def apply_encoded_filter_value(logger, request_options, value): # from apply_options, which expects an un-encoded input, # or from apply_url_params via apply_encoded_filter_value which decodes the input @staticmethod - def apply_filter_value(logger, request_options: TSC.PDFRequestOptions, value: str) -> None: + def apply_filter_value(logger, request_options: RequestOptions, value: str) -> None: logger.debug("handling filter param {}".format(value)) data_filter = value.split("=") request_options.vf(data_filter[0], data_filter[1]) # this is called from within from_url_params, for each param value @staticmethod - def apply_options_in_url(logger, request_options: TSC.PDFRequestOptions, value: str) -> None: + def apply_options_in_url(logger, request_options: RequestOptions, value: str) -> None: logger.debug("handling url option {}".format(value)) setting = value.split("=") if ":iid" == setting[0]: diff --git a/tabcmd/commands/datasources_and_workbooks/export_command.py b/tabcmd/commands/datasources_and_workbooks/export_command.py index cfecb387..d8102fcb 100644 --- a/tabcmd/commands/datasources_and_workbooks/export_command.py +++ b/tabcmd/commands/datasources_and_workbooks/export_command.py @@ -4,7 +4,8 @@ from tabcmd.commands.constants import Errors from tabcmd.execution.localize import _ from tabcmd.execution.logger_config import log -from .datasources_and_workbooks_command import DatasourcesAndWorkbooks +from .datasources_and_workbooks_command import DatasourcesAndWorkbooks, RequestOptions + pagesize = TSC.PDFRequestOptions.PageType # type alias for brevity @@ -120,8 +121,9 @@ def run_command(cls, args): except Exception as e: Errors.exit_with_error(logger, "Error saving to file", e) + # TODO should make the ability to pass in filters as args available in GET command too? @staticmethod - def apply_filters_from_args(request_options: TSC.PDFRequestOptions, args, logger=None) -> None: + def apply_filters_from_args(request_options: RequestOptions, args, logger) -> None: if args.filter: logger.debug("filter = {}".format(args.filter)) params = args.filter.split("&")