diff --git a/.gitattributes b/.gitattributes index ade44ab7c..040321c04 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ tableauserverclient/_version.py export-subst +tableauserverclient/bin/_version.py export-subst diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index cae0f409c..052113e7c 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -1,9 +1,12 @@ name: Publish to PyPi -# This will publish a package to TestPyPi (and real Pypi if run on master) with a version -# number generated by versioneer from the most recent tag looking like v____ -# TODO: maybe move this into the package job so all release-based actions are together +# This will build a package with a version set by versioneer from the most recent tag matching v____ +# It will publish to TestPyPi, and to real Pypi *if* run on master where head has a release tag +# For a live run, this should only need to be triggered by a newly published repo release. +# This can also be run manually for testing on: + release: + types: [published] workflow_dispatch: push: tags: @@ -23,7 +26,7 @@ jobs: - name: Build dist files run: | python -m pip install --upgrade pip - pip install -e .[test] build + python -m pip install -e .[test] build python -m build git describe --tag --dirty --always diff --git a/MANIFEST.in b/MANIFEST.in index 9b7512fb9..7acbed103 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,8 +4,6 @@ include CONTRIBUTORS.md include LICENSE include LICENSE.versioneer include README.md -include tableauserverclient/_version.py -include versioneer.py recursive-include docs *.md recursive-include samples *.py recursive-include samples *.txt @@ -18,5 +16,4 @@ recursive-include test *.png recursive-include test *.py recursive-include test *.xml recursive-include test *.tde -global-include *.pyi global-include *.typed diff --git a/publish.sh b/publish.sh deleted file mode 100755 index 46d54a1ee..000000000 --- a/publish.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -# tag the release version and confirm a clean version number -git tag vxxxx -git describe --tag --dirty --always - -set -e - -rm -rf dist -python setup.py sdist bdist_wheel -twine upload dist/* diff --git a/pyproject.toml b/pyproject.toml index 68f7589ca..3118182cb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=75.0", "versioneer[toml]==0.29", "wheel"] +requires = ["setuptools>=77.0", "versioneer[toml]==0.29", "wheel"] build-backend = "setuptools.build_meta" [project] @@ -8,7 +8,7 @@ name="tableauserverclient" dynamic = ["version"] description='A Python module for working with the Tableau Server REST API.' authors = [{name="Tableau", email="github@tableau.com"}] -license = {file = "LICENSE"} +license-files = ["LICENSE"] readme = "README.md" dependencies = [ @@ -35,6 +35,12 @@ repository = "https://github.com/tableau/server-client-python" test = ["black==24.8", "build", "mypy==1.4", "pytest>=7.0", "pytest-cov", "pytest-subtests", "requests-mock>=1.0,<2.0"] +[tool.setuptools.packages.find] +where = ["tableauserverclient", "tableauserverclient.helpers", "tableauserverclient.models", "tableauserverclient.server", "tableauserverclient.server.endpoint"] + +[tool.setuptools.dynamic] +version = {attr = "versioneer.get_version"} + [tool.black] line-length = 120 target-version = ['py39', 'py310', 'py311', 'py312', 'py313'] @@ -61,5 +67,5 @@ addopts = "--junitxml=./test.junit.xml" VCS = "git" style = "pep440-pre" versionfile_source = "tableauserverclient/bin/_version.py" -versionfile_build = "tableauserverclient/bin/_version.py" +versionfile_build = "_version.py" tag_prefix = "v" diff --git a/setup.py b/setup.py index bdce51f2e..b52ba267e 100644 --- a/setup.py +++ b/setup.py @@ -2,14 +2,7 @@ from setuptools import setup setup( - version=versioneer.get_version(), - cmdclass=versioneer.get_cmdclass(), - # not yet sure how to move this to pyproject.toml - packages=[ - "tableauserverclient", - "tableauserverclient.helpers", - "tableauserverclient.models", - "tableauserverclient.server", - "tableauserverclient.server.endpoint", - ], + # This line is required to set the version number when building the wheel + # not yet sure how to move this to pyproject.toml - it may require work in versioneer + cmdclass=versioneer.get_cmdclass() ) diff --git a/tableauserverclient/__init__.py b/tableauserverclient/__init__.py index 21e2c4760..6de77943a 100644 --- a/tableauserverclient/__init__.py +++ b/tableauserverclient/__init__.py @@ -139,7 +139,3 @@ "WeeklyInterval", "WorkbookItem", ] - -from .bin import _version - -__version__ = _version.get_versions()["version"] diff --git a/tableauserverclient/bin/__init__.py b/tableauserverclient/bin/__init__.py new file mode 100644 index 000000000..e4605a43b --- /dev/null +++ b/tableauserverclient/bin/__init__.py @@ -0,0 +1,3 @@ +# generated during initial setup of versioneer +from . import _version +__version__ = _version.get_versions()['version'] diff --git a/tableauserverclient/bin/_version.py b/tableauserverclient/bin/_version.py index f23819e86..680304c7d 100644 --- a/tableauserverclient/bin/_version.py +++ b/tableauserverclient/bin/_version.py @@ -46,14 +46,13 @@ class VersioneerConfig: def get_config() -> VersioneerConfig: """Create, populate and return the VersioneerConfig() object.""" - # these strings are filled in when 'setup.py versioneer' creates - # _version.py + # these strings are filled in from pyproject.toml at file generation time cfg = VersioneerConfig() cfg.VCS = "git" cfg.style = "pep440-pre" cfg.tag_prefix = "v" cfg.parentdir_prefix = "None" - cfg.versionfile_source = "tableauserverclient/_version.py" + cfg.versionfile_source = "tableauserverclient/bin/_version.py" cfg.verbose = False return cfg diff --git a/tableauserverclient/models/column_item.py b/tableauserverclient/models/column_item.py index 3a7416e28..9691bbef1 100644 --- a/tableauserverclient/models/column_item.py +++ b/tableauserverclient/models/column_item.py @@ -4,13 +4,13 @@ class ColumnItem: - def __init__(self, name, description=None): + def __init__(self, name=None, description=None): self._id = None self.description = description - self.name = name + self._name = name def __repr__(self): - return f"<{self.__class__.__name__} {self._id} {self.name} {self.description}>" + return f"<{self.__class__.__name__} {self._id} {self._name} {self.description}>" @property def id(self): diff --git a/test/http/__init__.py b/test/http/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/models/__init__.py b/test/models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/models/test_repr.py b/test/models/test_repr.py index 92d11978f..ed8f5ce9b 100644 --- a/test/models/test_repr.py +++ b/test/models/test_repr.py @@ -1,14 +1,13 @@ import inspect from unittest import TestCase -import _models # type: ignore # did not set types for this +import tableauserverclient.models as TSC_models # type: ignore # did not set types for this import tableauserverclient as TSC from typing import Any -# ensure that all models that don't need parameters can be instantiated -# todo.... +# ensure that all models can be instantiated def instantiate_class(name: str, obj: Any): # Get the constructor (init) of the class constructor = getattr(obj, "__init__", None) @@ -31,14 +30,38 @@ def instantiate_class(name: str, obj: Any): print(f"Class '{name}' does not have a constructor (__init__ method).") +not_yet_done = [ + "DQWItem", + "UnpopulatedPropertyError", + "FavoriteItem", + "FileuploadItem", + "FlowRunItem", + "IntervalItem", + "LinkedTaskItem", + "LinkedTaskStepItem", + "LinkedTaskFlowRunItem", + "Permission", + "SiteAuthConfiguration", + "Resource", + "TagItem", + "ExtractItem", +] + + class TestAllModels(TestCase): - # not all models have __repr__ yet: see above list + + # confirm that all models can be instantiated without params, and have __repr__ implemented + # not all do have __repr__ yet: see above list 'not_yet_done' def test_repr_is_implemented(self): - m = _models.get_defined_models() - for model in m: - with self.subTest(model.__name__, model=model): - print(model.__name__, type(model.__repr__).__name__) - self.assertEqual(type(model.__repr__).__name__, "function") + m = TSC_models + for type_name in m.__dict__: + if type_name in not_yet_done: + continue + model = getattr(m, type_name) + if inspect.isclass(model): + with self.subTest(type_name): + self.assertTrue(hasattr(model, "__repr__")) + self.assertEqual(type(model.__repr__).__name__, "function") # 2 - Iterate through the objects in the module def test_by_reflection(self):