tabpy 2.12.0__tar.gz → 2.13.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tabpy-2.12.0 → tabpy-2.13.0}/.gitignore +1 -1
- {tabpy-2.12.0 → tabpy-2.13.0}/CHANGELOG +7 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/PKG-INFO +8 -1
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/tabpy-tools.md +10 -0
- tabpy-2.13.0/tabpy/VERSION +1 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/endpoint_handler.py +4 -2
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/management/state.py +6 -3
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/client.py +87 -1
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/custom_query_object.py +11 -5
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/query_object.py +2 -2
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/rest_client.py +2 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/PKG-INFO +8 -1
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/tools_tests/test_client.py +28 -3
- tabpy-2.12.0/tabpy/VERSION +0 -1
- {tabpy-2.12.0 → tabpy-2.13.0}/.github/ISSUE_TEMPLATE/tabpy-issue-report.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/.github/workflows/coverage.yml +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/.github/workflows/docker-publish.yml +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/.github/workflows/lint.yml +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/.github/workflows/pull_request.yml +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/.github/workflows/push.yml +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/.pep8speaks.yml +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/.scrutinizer.yml +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/CONTRIBUTING.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/Dockerfile +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/LICENSE +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/MANIFEST.in +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/Procfile +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/README.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/_config.yml +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/app.json +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/codeql-analysis.yml +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/TableauConfiguration.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/about.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/deploy-to-heroku.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/img/Example1-SimpleFunctionCall.png +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/img/Example2-MultipleFunctionCalls.png +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/img/python-calculated-field.png +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/security.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/server-config.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/server-install.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/server-rest.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/docs/tabpy-virtualenv.md +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/misc/TabPy.postman_collection.json +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/misc/TabPy.yml +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/requirements.txt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/requirements_dev.txt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/setup.cfg +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/setup.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/start.sh +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/deploy_models.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/scripts/ANOVA.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/scripts/PCA.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/scripts/SentimentAnalysis.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/scripts/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/scripts/tTest.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/utils/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/utils/setup_utils.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/app/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/app/app.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/app/app_parameters.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/app/arrow_server.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/app/util.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/common/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/common/default.conf +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/common/endpoint_file_mgr.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/common/messages.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/common/util.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/base_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/basic_auth_server_middleware_factory.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/endpoints_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/evaluation_plane_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/management_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/no_op_auth_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/query_plane_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/service_info_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/status_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/upload_destination_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/util.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/management/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/management/util.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/psws/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/psws/callbacks.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/psws/python_service.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/state.ini.template +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/static/TabPy_logo.png +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/static/index.html +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/static/tableau.png +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/rest.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/schema.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/utils/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/utils/tabpy_user.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/SOURCES.txt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/dependency_links.txt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/entry_points.txt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/requires.txt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/top_level.txt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/integ_test_base.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/2019_04_24_to_3018_08_25.crt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/2019_04_24_to_3018_08_25.key +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/data.csv +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/deploy_and_evaluate_model.conf +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/deploy_and_evaluate_model_auth.conf +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/pwdfile.txt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_arrow_server.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_auth.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_custom_evaluate_timeout.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_and_evaluate_model.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_and_evaluate_model_auth_on.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_and_evaluate_model_ssl.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_model_ssl_off_auth_off.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_model_ssl_off_auth_on.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_model_ssl_on_auth_off.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_model_ssl_on_auth_on.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_evaluate.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_gzip.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_minimum_tls_version.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_url.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_url_ssl.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/resources/expired.crt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/resources/future.crt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/resources/valid.crt +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_config.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_endpoint_file_manager.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_endpoint_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_endpoints_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_evaluation_plane_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_pwd_file.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_service_info_handler.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/tools_tests/__init__.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/tools_tests/test_rest.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/tools_tests/test_rest_object.py +0 -0
- {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/tools_tests/test_schema.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tabpy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.13.0
|
|
4
4
|
Summary: Web server Tableau uses to run Python scripts.
|
|
5
5
|
Home-page: https://github.com/tableau/TabPy
|
|
6
6
|
Download-URL: https://pypi.org/project/tabpy
|
|
@@ -67,6 +67,13 @@ scripts and saved functions via Tableau's table calculations.
|
|
|
67
67
|
|
|
68
68
|
# Changelog
|
|
69
69
|
|
|
70
|
+
## v2.13.0
|
|
71
|
+
|
|
72
|
+
### Improvements
|
|
73
|
+
|
|
74
|
+
- Add support for deploying functions to a remote TabPy server by setting
|
|
75
|
+
`remote_server=True` when creating the Client instance.
|
|
76
|
+
|
|
70
77
|
## v2.12.0
|
|
71
78
|
|
|
72
79
|
### Improvements
|
|
@@ -41,6 +41,16 @@ The URL and port are where the Tableau-Python-Server process has been started -
|
|
|
41
41
|
more info can be found in the
|
|
42
42
|
[Starting TabPy](server-install.md#starting-tabpy) section of the documentation.
|
|
43
43
|
|
|
44
|
+
When connecting to a remote TabPy server, configure the following parameters:
|
|
45
|
+
|
|
46
|
+
- Set `remote_server` to `True` to indicate a remote connection
|
|
47
|
+
- Set `localhost_endpoint` to the specific localhost address used by the remote server
|
|
48
|
+
- **Note:** The protocol and port may differ from the main endpoint
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
client = Client('https://example.com:443/', remote_server=True, localhost_endpoint='http://localhost:9004/')
|
|
52
|
+
```
|
|
53
|
+
|
|
44
54
|
## Authentication
|
|
45
55
|
|
|
46
56
|
When TabPy is configured with the authentication feature on, client code
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.13.0
|
|
@@ -115,11 +115,13 @@ class EndpointHandler(ManagementHandler):
|
|
|
115
115
|
|
|
116
116
|
# delete files
|
|
117
117
|
if endpoint_info["type"] != "alias":
|
|
118
|
-
|
|
118
|
+
query_path = get_query_object_path(
|
|
119
119
|
self.settings["state_file_path"], name, None
|
|
120
120
|
)
|
|
121
|
+
staging_path = query_path.replace("/query_objects/", "/staging/endpoints/")
|
|
121
122
|
try:
|
|
122
|
-
yield self._delete_po_future(
|
|
123
|
+
yield self._delete_po_future(query_path)
|
|
124
|
+
yield self._delete_po_future(staging_path)
|
|
123
125
|
except Exception as e:
|
|
124
126
|
self.error_out(400, f"Error while deleting: {e}")
|
|
125
127
|
self.finish()
|
|
@@ -215,7 +215,7 @@ class TabPyState:
|
|
|
215
215
|
Name of the endpoint
|
|
216
216
|
description : str, optional
|
|
217
217
|
Description of this endpoint
|
|
218
|
-
|
|
218
|
+
docstring : str, optional
|
|
219
219
|
The doc string for this endpoint, if needed.
|
|
220
220
|
endpoint_type : str
|
|
221
221
|
The endpoint type (model, alias)
|
|
@@ -309,7 +309,7 @@ class TabPyState:
|
|
|
309
309
|
Name of the endpoint
|
|
310
310
|
description : str, optional
|
|
311
311
|
Description of this endpoint
|
|
312
|
-
|
|
312
|
+
docstring : str, optional
|
|
313
313
|
The doc string for this endpoint, if needed.
|
|
314
314
|
endpoint_type : str, optional
|
|
315
315
|
The endpoint type (model, alias)
|
|
@@ -340,7 +340,10 @@ class TabPyState:
|
|
|
340
340
|
endpoint_type, endpoint_info["type"])
|
|
341
341
|
dependencies = self._check_and_set_dependencies(
|
|
342
342
|
dependencies, endpoint_info.get("dependencies", []))
|
|
343
|
-
is_public
|
|
343
|
+
# Adding is_public means that some existing functions do not have is_public set.
|
|
344
|
+
# We need to check for this when updating and set to False by default
|
|
345
|
+
is_public = self._check_and_set_is_public(
|
|
346
|
+
is_public, getattr(endpoint_info, "is_public", False))
|
|
344
347
|
|
|
345
348
|
self._check_target(target)
|
|
346
349
|
if target and target not in endpoints:
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import copy
|
|
2
|
+
import inspect
|
|
2
3
|
from re import compile
|
|
3
4
|
import time
|
|
4
5
|
import requests
|
|
@@ -49,7 +50,9 @@ def _check_endpoint_name(name):
|
|
|
49
50
|
|
|
50
51
|
|
|
51
52
|
class Client:
|
|
52
|
-
def __init__(
|
|
53
|
+
def __init__(
|
|
54
|
+
self, endpoint, query_timeout=1000, remote_server=False, localhost_endpoint=None
|
|
55
|
+
):
|
|
53
56
|
"""
|
|
54
57
|
Connects to a running server.
|
|
55
58
|
|
|
@@ -63,10 +66,19 @@ class Client:
|
|
|
63
66
|
|
|
64
67
|
query_timeout : float, optional
|
|
65
68
|
The timeout for query operations.
|
|
69
|
+
|
|
70
|
+
remote_server : bool, optional
|
|
71
|
+
Whether client is a remote TabPy server.
|
|
72
|
+
|
|
73
|
+
localhost_endpoint : str, optional
|
|
74
|
+
The localhost endpoint with potentially different protocol and
|
|
75
|
+
port compared to the main endpoint parameter.
|
|
66
76
|
"""
|
|
67
77
|
_check_hostname(endpoint)
|
|
68
78
|
|
|
69
79
|
self._endpoint = endpoint
|
|
80
|
+
self._remote_server = remote_server
|
|
81
|
+
self._localhost_endpoint = localhost_endpoint
|
|
70
82
|
|
|
71
83
|
session = requests.session()
|
|
72
84
|
session.verify = False
|
|
@@ -232,6 +244,12 @@ class Client:
|
|
|
232
244
|
--------
|
|
233
245
|
remove, get_endpoints
|
|
234
246
|
"""
|
|
247
|
+
if self._remote_server:
|
|
248
|
+
return self._remote_deploy(
|
|
249
|
+
name, obj,
|
|
250
|
+
description=description, schema=schema, override=override, is_public=is_public
|
|
251
|
+
)
|
|
252
|
+
|
|
235
253
|
endpoint = self.get_endpoints().get(name)
|
|
236
254
|
version = 1
|
|
237
255
|
if endpoint:
|
|
@@ -390,6 +408,7 @@ class Client:
|
|
|
390
408
|
"methods": endpoint_object.get_methods(),
|
|
391
409
|
"required_files": [],
|
|
392
410
|
"required_packages": [],
|
|
411
|
+
"docstring": endpoint_object.get_docstring(),
|
|
393
412
|
"schema": copy.copy(schema),
|
|
394
413
|
"is_public": is_public,
|
|
395
414
|
}
|
|
@@ -419,6 +438,7 @@ class Client:
|
|
|
419
438
|
logger.info(
|
|
420
439
|
f"Waiting for endpoint {endpoint_name} to deploy to " f"version {version}"
|
|
421
440
|
)
|
|
441
|
+
time.sleep(interval)
|
|
422
442
|
start = time.time()
|
|
423
443
|
while True:
|
|
424
444
|
ep_status = self.get_status()
|
|
@@ -447,6 +467,72 @@ class Client:
|
|
|
447
467
|
logger.info(f"Sleeping {interval}...")
|
|
448
468
|
time.sleep(interval)
|
|
449
469
|
|
|
470
|
+
def _remote_deploy(
|
|
471
|
+
self, name, obj, description="", schema=None, override=False, is_public=False
|
|
472
|
+
):
|
|
473
|
+
"""
|
|
474
|
+
Remotely deploy a Python function using the /evaluate endpoint. Takes the same inputs
|
|
475
|
+
as deploy.
|
|
476
|
+
"""
|
|
477
|
+
remote_script = self._gen_remote_script()
|
|
478
|
+
remote_script += f"{inspect.getsource(obj)}\n"
|
|
479
|
+
|
|
480
|
+
remote_script += (
|
|
481
|
+
f"client.deploy("
|
|
482
|
+
f"'{name}', {obj.__name__}, '{description}', "
|
|
483
|
+
f"override={override}, is_public={is_public}, schema={schema}"
|
|
484
|
+
f")"
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
return self._evaluate_remote_script(remote_script)
|
|
488
|
+
|
|
489
|
+
def _gen_remote_script(self):
|
|
490
|
+
"""
|
|
491
|
+
Generates a remote script for TabPy client connection with credential handling.
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
str: A Python script to establish a TabPy client connection
|
|
495
|
+
"""
|
|
496
|
+
remote_script = [
|
|
497
|
+
"from tabpy.tabpy_tools.client import Client",
|
|
498
|
+
f"client = Client('{self._localhost_endpoint or self._endpoint}')"
|
|
499
|
+
]
|
|
500
|
+
|
|
501
|
+
remote_script.append(
|
|
502
|
+
f"client.set_credentials('{auth.username}', '{auth.password}')"
|
|
503
|
+
) if (auth := self._service.service_client.network_wrapper.auth) else None
|
|
504
|
+
|
|
505
|
+
return "\n".join(remote_script) + "\n"
|
|
506
|
+
|
|
507
|
+
def _evaluate_remote_script(self, remote_script):
|
|
508
|
+
"""
|
|
509
|
+
Uses TabPy /evaluate endpoint to execute a remote TabPy client script.
|
|
510
|
+
|
|
511
|
+
Parameters
|
|
512
|
+
----------
|
|
513
|
+
remote_script : str
|
|
514
|
+
The script to execute remotely.
|
|
515
|
+
"""
|
|
516
|
+
print(f"Remote script:\n{remote_script}\n")
|
|
517
|
+
url = f"{self._endpoint}evaluate"
|
|
518
|
+
headers = {"Content-Type": "application/json"}
|
|
519
|
+
payload = {"data": {}, "script": remote_script}
|
|
520
|
+
|
|
521
|
+
response = requests.post(
|
|
522
|
+
url,
|
|
523
|
+
headers=headers,
|
|
524
|
+
auth=self._service.service_client.network_wrapper.auth,
|
|
525
|
+
json=payload
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
msg = response.text.replace('null', 'Success')
|
|
529
|
+
if "Ad-hoc scripts have been disabled" in msg:
|
|
530
|
+
msg += "\n[Deployment to remote tabpy client not allowed.]"
|
|
531
|
+
|
|
532
|
+
status_message = (f"{response.status_code} - {msg}\n")
|
|
533
|
+
print(status_message)
|
|
534
|
+
return status_message
|
|
535
|
+
|
|
450
536
|
def set_credentials(self, username, password):
|
|
451
537
|
"""
|
|
452
538
|
Set credentials for all the TabPy client-server communication
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import platform
|
|
3
|
+
import sys
|
|
2
4
|
from .query_object import QueryObject as _QueryObject
|
|
3
5
|
|
|
4
6
|
|
|
@@ -69,12 +71,16 @@ class CustomQueryObject(_QueryObject):
|
|
|
69
71
|
)
|
|
70
72
|
raise
|
|
71
73
|
|
|
72
|
-
def
|
|
74
|
+
def get_docstring(self):
|
|
73
75
|
"""Get doc string from customized query"""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
default_docstring = "-- no docstring found in query function --"
|
|
77
|
+
|
|
78
|
+
# TODO: fix docstring parsing on Windows systems
|
|
79
|
+
if sys.platform == 'win32':
|
|
80
|
+
return default_docstring
|
|
81
|
+
|
|
82
|
+
ds = getattr(self.custom_query, '__doc__', None)
|
|
83
|
+
return ds if ds and isinstance(ds, str) else default_docstring
|
|
78
84
|
|
|
79
85
|
def get_methods(self):
|
|
80
86
|
return [self.get_query_method()]
|
|
@@ -14,7 +14,7 @@ class QueryObject(abc.ABC):
|
|
|
14
14
|
"""
|
|
15
15
|
Derived class needs to implement the following interface:
|
|
16
16
|
* query() -- given input, return query result
|
|
17
|
-
*
|
|
17
|
+
* get_docstring() -- returns documentation for the Query Object
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
def __init__(self, description=""):
|
|
@@ -30,7 +30,7 @@ class QueryObject(abc.ABC):
|
|
|
30
30
|
pass
|
|
31
31
|
|
|
32
32
|
@abc.abstractmethod
|
|
33
|
-
def
|
|
33
|
+
def get_docstring(self):
|
|
34
34
|
"""Returns documentation for the query object
|
|
35
35
|
|
|
36
36
|
By default, this method returns the docstring for 'query' method
|
|
@@ -41,6 +41,7 @@ class Endpoint(RESTObject):
|
|
|
41
41
|
version = RESTProperty(int)
|
|
42
42
|
description = RESTProperty(str)
|
|
43
43
|
dependencies = RESTProperty(list)
|
|
44
|
+
docstring = RESTProperty(str)
|
|
44
45
|
methods = RESTProperty(list)
|
|
45
46
|
creation_time = RESTProperty(datetime, from_epoch, to_epoch)
|
|
46
47
|
last_modified_time = RESTProperty(datetime, from_epoch, to_epoch)
|
|
@@ -64,6 +65,7 @@ class Endpoint(RESTObject):
|
|
|
64
65
|
and self.version == other.version
|
|
65
66
|
and self.description == other.description
|
|
66
67
|
and self.dependencies == other.dependencies
|
|
68
|
+
and self.docstring == other.docstring
|
|
67
69
|
and self.methods == other.methods
|
|
68
70
|
and self.evaluator == other.evaluator
|
|
69
71
|
and self.schema_version == other.schema_version
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tabpy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.13.0
|
|
4
4
|
Summary: Web server Tableau uses to run Python scripts.
|
|
5
5
|
Home-page: https://github.com/tableau/TabPy
|
|
6
6
|
Download-URL: https://pypi.org/project/tabpy
|
|
@@ -67,6 +67,13 @@ scripts and saved functions via Tableau's table calculations.
|
|
|
67
67
|
|
|
68
68
|
# Changelog
|
|
69
69
|
|
|
70
|
+
## v2.13.0
|
|
71
|
+
|
|
72
|
+
### Improvements
|
|
73
|
+
|
|
74
|
+
- Add support for deploying functions to a remote TabPy server by setting
|
|
75
|
+
`remote_server=True` when creating the Client instance.
|
|
76
|
+
|
|
70
77
|
## v2.12.0
|
|
71
78
|
|
|
72
79
|
### Improvements
|
|
@@ -12,18 +12,25 @@ class TestClient(unittest.TestCase):
|
|
|
12
12
|
|
|
13
13
|
def test_init(self):
|
|
14
14
|
client = Client("http://example.com:9004")
|
|
15
|
-
|
|
16
15
|
self.assertEqual(client._endpoint, "http://example.com:9004")
|
|
16
|
+
self.assertEqual(client._remote_server, False)
|
|
17
17
|
|
|
18
18
|
client = Client("http://example.com/", 10.0)
|
|
19
|
-
|
|
20
19
|
self.assertEqual(client._endpoint, "http://example.com/")
|
|
21
20
|
|
|
22
21
|
client = Client(endpoint="https://example.com/", query_timeout=-10.0)
|
|
23
|
-
|
|
24
22
|
self.assertEqual(client._endpoint, "https://example.com/")
|
|
25
23
|
self.assertEqual(client.query_timeout, 0.0)
|
|
26
24
|
|
|
25
|
+
client = Client(
|
|
26
|
+
"http://example.com:442/",
|
|
27
|
+
remote_server=True,
|
|
28
|
+
localhost_endpoint="http://localhost:9004/"
|
|
29
|
+
)
|
|
30
|
+
self.assertEqual(client._endpoint, "http://example.com:442/")
|
|
31
|
+
self.assertEqual(client._remote_server, True)
|
|
32
|
+
self.assertEqual(client._localhost_endpoint, "http://localhost:9004/")
|
|
33
|
+
|
|
27
34
|
# valid name tests
|
|
28
35
|
with self.assertRaises(ValueError):
|
|
29
36
|
Client("")
|
|
@@ -90,3 +97,21 @@ class TestClient(unittest.TestCase):
|
|
|
90
97
|
f"endpoint name {endpoint_name } can only contain: "
|
|
91
98
|
"a-z, A-Z, 0-9, underscore, hyphens and spaces.",
|
|
92
99
|
)
|
|
100
|
+
|
|
101
|
+
def test_deploy_with_remote_server(self):
|
|
102
|
+
client = Client("http://example.com:9004/", remote_server=True)
|
|
103
|
+
mock_evaluate_remote_script = Mock()
|
|
104
|
+
client._evaluate_remote_script = mock_evaluate_remote_script
|
|
105
|
+
client.deploy('name', lambda: True, 'description')
|
|
106
|
+
mock_evaluate_remote_script.assert_called()
|
|
107
|
+
|
|
108
|
+
def test_gen_remote_script(self):
|
|
109
|
+
client = Client("http://example.com:9004/", remote_server=True)
|
|
110
|
+
script = client._gen_remote_script()
|
|
111
|
+
self.assertTrue("from tabpy.tabpy_tools.client import Client" in script)
|
|
112
|
+
self.assertTrue("client = Client('http://example.com:9004/')" in script)
|
|
113
|
+
self.assertFalse("client.set_credentials" in script)
|
|
114
|
+
|
|
115
|
+
client.set_credentials("username", "password")
|
|
116
|
+
script = client._gen_remote_script()
|
|
117
|
+
self.assertTrue("client.set_credentials('username', 'password')" in script)
|
tabpy-2.12.0/tabpy/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.12.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/basic_auth_server_middleware_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/deploy_and_evaluate_model_auth.conf
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|