From 2daa03ad07eaef5d98ed9e463829af6401a7e4fc Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 21:36:24 -0600 Subject: [PATCH 01/12] support parsing TML into models --- src/thoughtspot_tml/tml.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/thoughtspot_tml/tml.py b/src/thoughtspot_tml/tml.py index 4cfae45..7dd16b5 100644 --- a/src/thoughtspot_tml/tml.py +++ b/src/thoughtspot_tml/tml.py @@ -176,6 +176,42 @@ class Worksheet(_tml.TML): def name(self) -> str: return self.worksheet.name + @property + def is_model(self) -> bool: + """ + Determines if the TML object is the newer Worksheet, a Model. + + Further reading: + https://docs.thoughtspot.com/cloud/latest/models + """ + return self.worksheet.schema is not None + + @classmethod + def _loads(cls, tml_document: str) -> Dict[str, Any]: + # Handle backwards incompatible changes. + + # DEV NOTE: @boonhapus, 2024/02/14 + # The Worksheet V2 update include a python reserved word in the spec, which + # python-betterproto automatically adds a trailing sunder to. This reverses it. + if "with:" in tml_document: + tml_document = tml_document.replace("- with:", "- with_:") + + return _yaml.load(tml_document) + + def _to_dict(self) -> Dict[str, Any]: + # Handle backwards incompatible changes. + data = asdict(self) + + # DEV NOTE: @boonhapus, 2024/02/14 + # The Worksheet V2 update include a python reserved word in the spec, which + # python-betterproto automatically adds a trailing sunder to. This reverses it. + if self.is_model: + text = json.dumps(data) + text = text.replace('"with_"', '"with"') + data = json.loads(text) + + return data + @dataclass class Answer(_tml.TML): From 6bf9c83edadc636a7718e0db0c1ae19b4d81892f Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 21:37:10 -0600 Subject: [PATCH 02/12] add tests for models --- tests/_const.py | 1 + tests/data/DUMMY_MODEL.worksheet.tml | 151 +++++++++++++++++++++++++++ tests/test_worksheet.py | 58 ++++++---- 3 files changed, 187 insertions(+), 23 deletions(-) create mode 100644 tests/data/DUMMY_MODEL.worksheet.tml diff --git a/tests/_const.py b/tests/_const.py index 151e355..264f9fd 100644 --- a/tests/_const.py +++ b/tests/_const.py @@ -11,6 +11,7 @@ DUMMY_VIEW = DATA_DIR / "DUMMY.view.tml" DUMMY_SQL_VIEW = DATA_DIR / "DUMMY.sql_view.tml" DUMMY_WORKSHEET = DATA_DIR / "DUMMY.worksheet.tml" +DUMMY_MODEL = DATA_DIR / "DUMMY_model.worksheet.tml" DUMMY_ANSWER = DATA_DIR / "DUMMY.answer.tml" DUMMY_PINBOARD = DATA_DIR / "DUMMY.pinboard.tml" DUMMY_LIVEBOARD = DATA_DIR / "DUMMY.liveboard.tml" diff --git a/tests/data/DUMMY_MODEL.worksheet.tml b/tests/data/DUMMY_MODEL.worksheet.tml new file mode 100644 index 0000000..5a6de44 --- /dev/null +++ b/tests/data/DUMMY_MODEL.worksheet.tml @@ -0,0 +1,151 @@ +guid: 5cb2b688-8531-45a4-9efb-35dbd2104a84 +worksheet: + name: Sales Model + description: Retails sales as a model. + schema: + tables: + - name: DIM_RETAPP_PRODUCTS + alias: PRODUCTS + - name: DIM_RETAPP_STORES + alias: STORES + - name: FACT_RETAPP_SALES + alias: SALES + joins: + - with: PRODUCTS + "on": "[FACT_RETAPP_SALES::Product ID] = [DIM_RETAPP_PRODUCTS::Product ID]" + type: RIGHT_OUTER + cardinality: MANY_TO_ONE + - with: STORES + "on": "[FACT_RETAPP_SALES::Store ID] = [DIM_RETAPP_STORES::Store ID]" + type: RIGHT_OUTER + cardinality: MANY_TO_ONE + formulas: + - id: Sales with tax + name: Sales with tax + expr: "( 1 + [Tax Rate] ) * [SALES::Sales Amount] " + worksheet_columns: + - name: Product Name + column_id: PRODUCTS::Product Name + properties: + column_type: ATTRIBUTE + index_type: DONT_INDEX + synonyms: + - Item Name + - Goods Name + synonym_type: AUTO_GENERATED + - name: Product Type + column_id: PRODUCTS::Product Type + properties: + column_type: ATTRIBUTE + index_type: DONT_INDEX + synonyms: + - Product Category + - Product Group + synonym_type: AUTO_GENERATED + - name: Item Price + column_id: SALES::Item Price + properties: + column_type: MEASURE + aggregation: SUM + index_type: DONT_INDEX + synonyms: + - Unit Price + - Unit Cost + currency_type: + iso_code: USD + synonym_type: AUTO_GENERATED + - name: Quantity Purchased + column_id: SALES::Quantity Purchased + properties: + column_type: MEASURE + aggregation: SUM + index_type: DONT_INDEX + synonyms: + - Quantity Bought + - Quantity Ordered + synonym_type: AUTO_GENERATED + - name: Sales Amount + column_id: SALES::Sales Amount + properties: + column_type: MEASURE + aggregation: SUM + index_type: DONT_INDEX + synonyms: + - Transaction Amount + - Purchase Amount + currency_type: + iso_code: USD + synonym_type: AUTO_GENERATED + - name: Sales Date + column_id: SALES::Sales Date + properties: + column_type: ATTRIBUTE + index_type: DONT_INDEX + synonyms: + - Transaction Date + - Purchase Date + synonym_type: AUTO_GENERATED + - name: City + column_id: STORES::City + properties: + column_type: ATTRIBUTE + index_type: DONT_INDEX + synonyms: + - Town + - Municipality + synonym_type: AUTO_GENERATED + - name: Region + column_id: STORES::Region + properties: + column_type: ATTRIBUTE + index_type: DONT_INDEX + synonyms: + - Area + - Zone + synonym_type: AUTO_GENERATED + - name: State + column_id: STORES::State + properties: + column_type: ATTRIBUTE + index_type: DONT_INDEX + synonyms: + - Province + - Territory + synonym_type: AUTO_GENERATED + - name: Store Name + column_id: STORES::Store Name + properties: + column_type: ATTRIBUTE + index_type: DONT_INDEX + synonyms: + - Outlet Name + - Retailer Name + synonym_type: AUTO_GENERATED + - name: Zip Code + column_id: STORES::Zip Code + properties: + column_type: ATTRIBUTE + index_type: DONT_INDEX + synonyms: + - Postal Code + - Postal Index + synonym_type: AUTO_GENERATED + - name: Sales with tax + formula_id: Sales with tax + properties: + column_type: MEASURE + aggregation: SUM + index_type: DONT_INDEX + synonyms: + - Transaction with Tax + - Purchase with Tax + synonym_type: AUTO_GENERATED + properties: + is_bypass_rls: false + join_progressive: true + parameters: + - id: cbcbf424-3b2d-402f-a372-32164674e162 + name: Tax Rate + data_type: DOUBLE + default_value: "0.0" + description: Sales tax rate to add to the cost. diff --git a/tests/test_worksheet.py b/tests/test_worksheet.py index d8ba874..b865e0d 100644 --- a/tests/test_worksheet.py +++ b/tests/test_worksheet.py @@ -4,26 +4,38 @@ from . import _const -@test("Worksheet deep attribute access") -def _(): - t = Worksheet.load(_const.DUMMY_WORKSHEET) - - assert type(t) is Worksheet - - t.guid - t.worksheet - t.worksheet.tables - t.worksheet.tables[0].name - t.worksheet.joins - t.worksheet.joins[0].type - t.worksheet.table_paths - t.worksheet.table_paths[0].table - t.worksheet.table_paths[0].join_path - t.worksheet.table_paths[0].join_path[0].join - t.worksheet.formulas - t.worksheet.formulas[0].expr - t.worksheet.worksheet_columns - t.worksheet.worksheet_columns[0].properties - t.worksheet.worksheet_columns[0].properties.index_type - t.worksheet.properties - t.worksheet.properties.is_bypass_rls +for version, file in (("V1", _const.DUMMY_WORKSHEET), ("V2", _const.DUMMY_MODEL)): + + @test("Worksheet {version} deep attribute access") + def _(file=file, version=version): + t = Worksheet.load(file) + + assert type(t) is Worksheet + + t.guid + t.worksheet + t.worksheet.formulas + t.worksheet.formulas[0].expr + t.worksheet.worksheet_columns + t.worksheet.worksheet_columns[0].properties + t.worksheet.worksheet_columns[0].properties.index_type + t.worksheet.properties + t.worksheet.properties.is_bypass_rls + + if version == "V1": + assert t.is_model is False + t.worksheet.tables + t.worksheet.tables[0].name + t.worksheet.joins + t.worksheet.joins[0].type + t.worksheet.table_paths + t.worksheet.table_paths[0].table + t.worksheet.table_paths[0].join_path + t.worksheet.table_paths[0].join_path[0].join + + if version == "V2": + assert t.is_model is True + t.worksheet.schema + t.worksheet.schema.tables + t.worksheet.schema.tables[0].name + t.worksheet.schema.tables[0].alias From 5a1057471b89618cfce6c64b36008855c88561d9 Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 21:38:21 -0600 Subject: [PATCH 03/12] drop black in favor of ruff --- pyproject.toml | 70 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 353c0dc..223264b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,6 @@ dev = [ # code quality "pre-commit", "ruff", - "black", "mypy", # tests @@ -75,17 +74,13 @@ exclude = ["_generate*"] [tool.setuptools.dynamic] version = {attr = "thoughtspot_tml._version.__version__"} -[tool.black] -line-length = 120 -exclude = ''' -( - __pycache__ # ignore compiled bytecode - | \.venv* # ignore virtual environments - | \.nox # ignore virtual environments -) -''' - [tool.mypy] +warn_unused_configs = true +warn_redundant_casts = true +warn_unused_ignores = true +check_untyped_defs = true +strict_equality = true +strict_concatenate = true exclude = ''' (?x)( ^_scriptability.py$ # ignore auto-generated files @@ -94,33 +89,62 @@ exclude = ''' [tool.ruff] line-length = 120 +src = ["src/thoughtspot_tml"] +exclude = [ + "__pycache__", # ignore compiled bytecode + ".venv*", # ignore virtual environments + ".nox", # ignore virtual environments + + # project specific ignores + "*compat.py", # ignore compatibles + "_scriptability.py", # ignore auto-generated files +] + +[tool.ruff.lint] select = [ "A", # flake8-builtins: https://pypi.org/project/flake8-builtins/ "ARG", # flake8-unused-arguments: https://pypi.org/project/flake8-unused-arguments/ "B", # flake8-bugbear: https://pypi.org/project/flake8-bugbear/ "C4", # flake8-comprehensions: https://pypi.org/project/flake8-comprehensions/ - "C90", # mccabe: https://pypi.org/project/mccabe/ "COM", # flake8-commas: https://pypi.org/project/flake8-commas/ + "DTZ", # flake8-datetimez: https://pypi.org/project/flake8-datetimez/ "E", # pycodestyle: https://pypi.org/project/pycodestyle/ "F", # pyflakes: https://pypi.org/project/pyflakes/ + "FA", # flake8-future-annotations: https://pypi.org/project/flake8-future-annotations/ + "I", # isort: https://pypi.org/project/isort/ "Q", # flake8-quotes: https://pypi.org/project/flake8-quotes/ "RUF", # ruff-specific: https://beta.ruff.rs/docs/rules/#ruff-specific-rules-ruf + "T20", # flake8-print: https://pypi.org/project/flake8-print/ "TCH", # flake8-type-checking: https://pypi.org/project/flake8-type-checking/ ] -src = ["src/thoughtspot_tml"] -exclude = [ - "__pycache__", # ignore compiled bytecode - ".venv*", # ignore virtual environments - ".nox", # ignore virtual environments +ignore-init-module-imports = true - # project specific ignores - "*compat.py", # ignore compatibles - "_scriptability.py", # ignore auto-generated files +[tool.ruff.lint.flake8-import-conventions.aliases] +# Declare the default aliases. +datetime = "dt" +sqlalchemy = "sa" + +[tool.ruff.lint.flake8-type-checking] +runtime-evaluated-base-classes = [ + "cs_tools.datastructures._GlobalModel", + "cs_tools.datastructures._GlobalSettings", + "cs_tools.datastructures.ValidatedSQLModel", + "typer.params.Option", + "typer.params.Argument", + "pydantic.BaseModel", + "pydantic_settings.BaseSettings", + "sqlalchemy.orm.DeclarativeBase", + "sqlmodel.SQLModel", ] -ignore-init-module-imports = true -[tool.ruff.per-file-ignores] -"types.py" = ["TCH001"] +[flake8] +type-checking-pydantic-enabled = true + +[tool.ruff.lint.isort] +combine-as-imports = true +force-wrap-aliases = true +from-first = true +required-imports = ["from __future__ import annotations"] [tool.ward] path = "tests/" From e77ed3dc5499f681c6da4be20ad36885206ffd18 Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 21:40:58 -0600 Subject: [PATCH 04/12] update pre-commit --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6c36f6d..08b755c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,13 +9,13 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: v0.1.6 + rev: v0.2.1 hooks: - id: ruff # Using this mirror lets us use mypyc-compiled black, which is about 2x faster - repo: https://github.com/psf/black-pre-commit-mirror - rev: 23.11.0 + rev: 24.2.0 hooks: - id: black From a448df5701773eff1734cac18fd1e107730e7c52 Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 21:42:23 -0600 Subject: [PATCH 05/12] notes --- src/thoughtspot_tml/_tml.py | 4 ++-- src/thoughtspot_tml/tml.py | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/thoughtspot_tml/_tml.py b/src/thoughtspot_tml/_tml.py index 61166bc..3366829 100644 --- a/src/thoughtspot_tml/_tml.py +++ b/src/thoughtspot_tml/_tml.py @@ -137,12 +137,12 @@ def __post_init__(self): @classmethod def _loads(cls, tml_document: str) -> Dict[str, Any]: # @boonhapus note: do not override this!! - # ONLY exists to enable a TML interface over Connections and fix SCAL-134095 + # These exist to handle backwards compatible changes between TML versions. return _yaml.load(tml_document) def _to_dict(self) -> Dict[str, Any]: # @boonhapus note: do not override this!! - # ONLY exists to enable a TML interface over Connections and fix SCAL-134095 + # These exist to handle backwards compatible changes between TML versions. return asdict(self) @classmethod diff --git a/src/thoughtspot_tml/tml.py b/src/thoughtspot_tml/tml.py index 7dd16b5..02b3677 100644 --- a/src/thoughtspot_tml/tml.py +++ b/src/thoughtspot_tml/tml.py @@ -28,8 +28,12 @@ def name(self) -> str: @classmethod def _loads(cls, tml_document): + # Handle backwards incompatible changes. document = _yaml.load(tml_document) + # DEV NOTE: @boonhapus, 2024/02/14 + # Connections do not offer a TML component, so we'll fake it. + if "guid" not in document: document = {"guid": None, "connection": document} @@ -37,10 +41,14 @@ def _loads(cls, tml_document): @classmethod def load(cls, path): + # Handle backwards incompatible changes. instance = super().load(path) + # DEV NOTE: @boonhapus, 2024/02/14 + # Connections do not offer a TML component, so we'll fake it. + try: - name, dot, ext = path.name.partition(".") + name, _, ext = path.name.partition(".") instance.guid = str(uuid.UUID(name, version=4)) except ValueError: pass @@ -48,6 +56,11 @@ def load(cls, path): return instance def _to_dict(self): + # Handle backwards incompatible changes. + + # DEV NOTE: @boonhapus, 2024/02/14 + # Connections do not offer a TML component, so we'll fake it. + data = asdict(self) return data["connection"] @@ -242,6 +255,7 @@ def name(self) -> str: @classmethod def _loads(cls, tml_document): + # Handle backwards incompatible changes. document = _yaml.load(tml_document) # @boonhapus, 2022/11/25 From 54c78e7aaaf15d9cab5015ebecc10f2877142b33 Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 21:50:56 -0600 Subject: [PATCH 06/12] remove black from pre-commit --- .pre-commit-config.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 08b755c..dbcade4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,15 +9,14 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: v0.2.1 + rev: v0.1.7 hooks: + # Run the linter. - id: ruff + args: [--fix] - # Using this mirror lets us use mypyc-compiled black, which is about 2x faster - - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.2.0 - hooks: - - id: black + # Run the formatter. + - id: ruff-format - repo: https://github.com/dhruvmanila/remove-print-statements rev: v0.5.2 From d5d9100ffd3a3cc3514ef4830189475f75e0a68f Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 21:55:07 -0600 Subject: [PATCH 07/12] update ruff ignores --- pyproject.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 223264b..c48a758 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,11 +96,14 @@ exclude = [ ".nox", # ignore virtual environments # project specific ignores + "__init__.py", # ignore __init__.py + "_version.py", # ignore __version__ "*compat.py", # ignore compatibles "_scriptability.py", # ignore auto-generated files ] [tool.ruff.lint] +ignore-init-module-imports = true select = [ "A", # flake8-builtins: https://pypi.org/project/flake8-builtins/ "ARG", # flake8-unused-arguments: https://pypi.org/project/flake8-unused-arguments/ @@ -117,7 +120,6 @@ select = [ "T20", # flake8-print: https://pypi.org/project/flake8-print/ "TCH", # flake8-type-checking: https://pypi.org/project/flake8-type-checking/ ] -ignore-init-module-imports = true [tool.ruff.lint.flake8-import-conventions.aliases] # Declare the default aliases. From 37a966e6f03fb9ee71d8374425c45cb58cf620fd Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 21:55:30 -0600 Subject: [PATCH 08/12] code quality --- src/thoughtspot_tml/_tml.py | 14 +++++++------- src/thoughtspot_tml/_yaml.py | 5 ++--- src/thoughtspot_tml/exceptions.py | 7 +++---- src/thoughtspot_tml/spotapp.py | 13 ++++++------- src/thoughtspot_tml/tml.py | 20 +++++++++++++++----- src/thoughtspot_tml/types.py | 17 ++++++++--------- src/thoughtspot_tml/utils.py | 18 ++++++++---------- 7 files changed, 49 insertions(+), 45 deletions(-) diff --git a/src/thoughtspot_tml/_tml.py b/src/thoughtspot_tml/_tml.py index 3366829..d271520 100644 --- a/src/thoughtspot_tml/_tml.py +++ b/src/thoughtspot_tml/_tml.py @@ -1,19 +1,19 @@ from __future__ import annotations from collections.abc import Collection -from typing import TYPE_CHECKING from dataclasses import asdict, dataclass, fields, is_dataclass -import warnings -import pathlib -import typing +from typing import TYPE_CHECKING import json +import pathlib import re +import typing +import warnings import yaml -from thoughtspot_tml.exceptions import TMLDecodeError, TMLExtensionWarning -from thoughtspot_tml._compat import get_origin, get_args from thoughtspot_tml import _scriptability, _yaml +from thoughtspot_tml._compat import get_args, get_origin +from thoughtspot_tml.exceptions import TMLDecodeError, TMLExtensionWarning if TYPE_CHECKING: from typing import Any, Dict @@ -22,7 +22,7 @@ RE_CAMEL_CASE = re.compile(r"[A-Z]?[a-z]+|[A-Z]{2,}(?=[A-Z][a-z]|\d|\W|$)|\d+") -def recursive_complex_attrs_to_dataclasses(instance: Any) -> None: # noqa: C901 +def recursive_complex_attrs_to_dataclasses(instance: Any) -> None: """ Convert all fields of type `dataclass` into an instance of the specified dataclass if the current value is a dict. diff --git a/src/thoughtspot_tml/_yaml.py b/src/thoughtspot_tml/_yaml.py index 7ddbe56..c2c78e9 100644 --- a/src/thoughtspot_tml/_yaml.py +++ b/src/thoughtspot_tml/_yaml.py @@ -1,10 +1,11 @@ +from __future__ import annotations + from math import inf as INFINITY from typing import Any, Dict import re import yaml - # TML column ids typically take the form.. # # LOGICAL_TABLE_NAME_#::LOGICAL_COLUMN_NAME @@ -52,8 +53,6 @@ def _double_quote_when_special_char(dumper: yaml.Dumper, data: str) -> yaml.Scal if (special and not is_tml_id) or reserved or empty_str: style = '"' - # elif len(data.splitlines()) > 1: - # style = "|" else: style = "" diff --git a/src/thoughtspot_tml/exceptions.py b/src/thoughtspot_tml/exceptions.py index 44b7e85..7e0f53d 100644 --- a/src/thoughtspot_tml/exceptions.py +++ b/src/thoughtspot_tml/exceptions.py @@ -1,17 +1,16 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Dict, Optional, Type import dataclasses -from typing import Any, Dict, Optional, Type - if TYPE_CHECKING: from collections.abc import Iterable from pathlib import Path - from thoughtspot_tml.types import TMLObject, GUID from yaml import error + from thoughtspot_tml.types import GUID, TMLObject + class TMLError(Exception): """ diff --git a/src/thoughtspot_tml/spotapp.py b/src/thoughtspot_tml/spotapp.py index e0e58a7..43ce0c5 100644 --- a/src/thoughtspot_tml/spotapp.py +++ b/src/thoughtspot_tml/spotapp.py @@ -1,24 +1,23 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Dict, List, Optional -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Dict, List, Optional +import json import pathlib import zipfile -import json +from thoughtspot_tml import _yaml from thoughtspot_tml._compat import ZipPath +from thoughtspot_tml.tml import Answer, Liveboard, SQLView, Table, View, Worksheet from thoughtspot_tml.utils import determine_tml_type -from thoughtspot_tml.tml import Table, View, SQLView, Worksheet, Answer, Liveboard -from thoughtspot_tml import _yaml if TYPE_CHECKING: - from thoughtspot_tml.types import EDocExportResponses, TMLObject, TMLDocInfo, SpotAppInfo + from thoughtspot_tml.types import EDocExportResponses, SpotAppInfo, TMLDocInfo, TMLObject @dataclass class Manifest: - object: List[TMLDocInfo] # noqa: A003 + object: List[TMLDocInfo] @dataclass diff --git a/src/thoughtspot_tml/tml.py b/src/thoughtspot_tml/tml.py index 02b3677..63552c6 100644 --- a/src/thoughtspot_tml/tml.py +++ b/src/thoughtspot_tml/tml.py @@ -1,16 +1,22 @@ from __future__ import annotations -from dataclasses import dataclass, asdict -from typing import TYPE_CHECKING +from dataclasses import asdict, dataclass +from typing import TYPE_CHECKING, Any, Dict import copy +import json import uuid -from thoughtspot_tml import _tml, _scriptability, _yaml +from thoughtspot_tml import _scriptability, _tml, _yaml if TYPE_CHECKING: from typing import Optional - from thoughtspot_tml.types import ConnectionMetadata, ExternalDatabase, ExternalSchema, GUID + from thoughtspot_tml.types import ( + GUID, + ConnectionMetadata, + ExternalDatabase, + ExternalSchema, + ) @dataclass @@ -74,7 +80,11 @@ def to_rest_api_v1_metadata(self) -> ConnectionMetadata: "configuration": {kv.key: kv.value for kv in self.connection.properties}, "externalDatabases": [], } - this_database: ExternalDatabase = {"name": None, "isAutoCreated": False, "schemas": []} + this_database: ExternalDatabase = { + "name": None, + "isAutoCreated": False, + "schemas": [], + } this_schema: ExternalSchema = {"name": None, "tables": []} # this connection has 0 tables (very popular "initial state" structure TS 9.0.0+) diff --git a/src/thoughtspot_tml/types.py b/src/thoughtspot_tml/types.py index e173d4c..29bd8a3 100644 --- a/src/thoughtspot_tml/types.py +++ b/src/thoughtspot_tml/types.py @@ -1,10 +1,9 @@ from __future__ import annotations -from typing import TYPE_CHECKING -from typing import Union, Type +from typing import TYPE_CHECKING, Type, Union -from thoughtspot_tml._compat import Literal, TypedDict, Annotated -from thoughtspot_tml.tml import Table, View, SQLView, Worksheet, Answer, Liveboard +from thoughtspot_tml._compat import Annotated, Literal, TypedDict +from thoughtspot_tml.tml import Answer, Liveboard, SQLView, Table, View, Worksheet if TYPE_CHECKING: from typing import Any, Dict, List, Optional @@ -42,8 +41,8 @@ class TMLDocInfo(TypedDict): name: str filename: str status: StatusCode - type: str # noqa: A003 - id: GUID # noqa: A003 + type: str + id: GUID dependency: List[FileInfo] @@ -53,7 +52,7 @@ class EDocExportResponse(TypedDict): class EDocExportResponses(TypedDict): - object: List[EDocExportResponse] # noqa: A003 + object: List[EDocExportResponse] # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /connection/* Metadata Data Structure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -61,7 +60,7 @@ class EDocExportResponses(TypedDict): class ExternalColumn(TypedDict): name: str - type: str # noqa: A003 + type: str canImport: bool selected: bool isLinkedActive: bool @@ -73,7 +72,7 @@ class ExternalColumn(TypedDict): class ExternalTable(TypedDict): name: str - type: str # noqa: A003 + type: str description: str selected: bool linked: bool diff --git a/src/thoughtspot_tml/utils.py b/src/thoughtspot_tml/utils.py index 4336525..66651b1 100644 --- a/src/thoughtspot_tml/utils.py +++ b/src/thoughtspot_tml/utils.py @@ -2,21 +2,19 @@ from dataclasses import fields, is_dataclass from typing import TYPE_CHECKING -import warnings +import json import logging import pathlib -import json +import warnings -from thoughtspot_tml.exceptions import TMLError, TMLDisambiguationError, MissingGUIDMappedValueWarning -from thoughtspot_tml.tml import Connection -from thoughtspot_tml.tml import Table, View, SQLView, Worksheet -from thoughtspot_tml.tml import Answer, Liveboard, Pinboard -from thoughtspot_tml import _scriptability, _compat +from thoughtspot_tml import _compat, _scriptability +from thoughtspot_tml.exceptions import MissingGUIDMappedValueWarning, TMLDisambiguationError, TMLError +from thoughtspot_tml.tml import Answer, Connection, Liveboard, Pinboard, SQLView, Table, View, Worksheet if TYPE_CHECKING: - from typing import Any, Callable, Dict, List, Set, Tuple, Union, Optional, Type, Iterator + from typing import Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Type, Union - from thoughtspot_tml.types import GUID, TMLObject, TMLDocInfo + from thoughtspot_tml.types import GUID, TMLDocInfo, TMLObject _UNDEFINED = object() @@ -192,7 +190,7 @@ def __getitem__(self, guid: GUID) -> Dict[str, GUID]: def __contains__(self, guid: GUID) -> bool: return bool(self.get(guid, default=False)) - def set(self, src_guid: GUID, *, environment: str, guid: GUID) -> None: # noqa: A003 + def set(self, src_guid: GUID, *, environment: str, guid: GUID) -> None: """ Insert a new GUID into the mapping. From 3d137874d9a756ba7a1d1c6b98d59ecd4871ac32 Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 22:01:19 -0600 Subject: [PATCH 09/12] unrestrict python 3.12 and add to ci --- .github/workflows/test.yml | 1 + pyproject.toml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c43a0c4..22bbf7a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,6 +24,7 @@ jobs: - "3.9" - "3.10" - "3.11" + - "3.12" steps: - uses: actions/checkout@v3 diff --git a/pyproject.toml b/pyproject.toml index c48a758..51549fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ name = "thoughtspot_tml" dynamic = ["version"] description = "Library for manipulating ThoughtSpot Modeling Language (TML) files" readme = "README.md" -requires-python = ">=3.7 , < 3.12" +requires-python = ">=3.7 , < 3.13" license = {file = "LICENSE"} authors = [ {name = "Bryant Howell", email = "bryant.howell@thoughtspot.com"}, @@ -28,6 +28,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ] dependencies = [ From 2982dd6d3e35cf9b53d01602aa2291f7e15f1470 Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 22:01:31 -0600 Subject: [PATCH 10/12] swap black for ruff in ci --- .github/workflows/lint.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 197bc4b..2db588d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,11 +7,8 @@ on: - v2_main jobs: - lint: + ruff: runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: psf/black@stable - with: - options: "--check --verbose" + - uses: actions/checkout@v3 + - uses: chartboost/ruff-action@v1 From d6ddee1811343078aaf5a771e323054f05e574ff Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 22:09:31 -0600 Subject: [PATCH 11/12] update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9b08861..b399552 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -10,7 +10,6 @@ Before submitting a pull request, please make sure the enhancement or bugfix you This will ensure no work is duplicated, and that a general approach has been agreed. - ## Local development setup ```shell @@ -21,15 +20,19 @@ pip install -e .[dev] ## Build -```shell -download edoc.proto from internal version control -python _generate\__main__.py -``` +> [!IMPORTANT] +> If you're writing support for a new version of ThoughtSpot, this step requires you to download `scriptability`'s EDoc protocol buffer specification from ThoughtSpot's internal version control. +> +> ```shell +> python _generate\__main__.py +> ``` + +If you're not building support for a new version of ThoughtSpot, simply **[Fork this repository](https://github.com/thoughtspot/thoughtspot_tml/fork)** and when ready, **[Open a Pull Request](https://github.com/thoughtspot/thoughtspot_tml/compare)** to contribute your changes. ## Linting ```shell -black . --config pyproject.toml +ruff check src/ --config pyproject.toml ``` ## Running Tests From 2f70a84896fc2675f043f7aefc860f546b52f59a Mon Sep 17 00:00:00 2001 From: boonhapus Date: Wed, 14 Feb 2024 22:11:02 -0600 Subject: [PATCH 12/12] version bump --> 2.0.14 --- src/thoughtspot_tml/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thoughtspot_tml/_version.py b/src/thoughtspot_tml/_version.py index 0610187..2ae4933 100644 --- a/src/thoughtspot_tml/_version.py +++ b/src/thoughtspot_tml/_version.py @@ -1 +1 @@ -__version__ = "2.0.13" +__version__ = "2.0.14"