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.
Files changed (143) hide show
  1. {tabpy-2.12.0 → tabpy-2.13.0}/.gitignore +1 -1
  2. {tabpy-2.12.0 → tabpy-2.13.0}/CHANGELOG +7 -0
  3. {tabpy-2.12.0 → tabpy-2.13.0}/PKG-INFO +8 -1
  4. {tabpy-2.12.0 → tabpy-2.13.0}/docs/tabpy-tools.md +10 -0
  5. tabpy-2.13.0/tabpy/VERSION +1 -0
  6. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/endpoint_handler.py +4 -2
  7. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/management/state.py +6 -3
  8. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/client.py +87 -1
  9. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/custom_query_object.py +11 -5
  10. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/query_object.py +2 -2
  11. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/rest_client.py +2 -0
  12. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/PKG-INFO +8 -1
  13. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/tools_tests/test_client.py +28 -3
  14. tabpy-2.12.0/tabpy/VERSION +0 -1
  15. {tabpy-2.12.0 → tabpy-2.13.0}/.github/ISSUE_TEMPLATE/tabpy-issue-report.md +0 -0
  16. {tabpy-2.12.0 → tabpy-2.13.0}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
  17. {tabpy-2.12.0 → tabpy-2.13.0}/.github/workflows/coverage.yml +0 -0
  18. {tabpy-2.12.0 → tabpy-2.13.0}/.github/workflows/docker-publish.yml +0 -0
  19. {tabpy-2.12.0 → tabpy-2.13.0}/.github/workflows/lint.yml +0 -0
  20. {tabpy-2.12.0 → tabpy-2.13.0}/.github/workflows/pull_request.yml +0 -0
  21. {tabpy-2.12.0 → tabpy-2.13.0}/.github/workflows/push.yml +0 -0
  22. {tabpy-2.12.0 → tabpy-2.13.0}/.pep8speaks.yml +0 -0
  23. {tabpy-2.12.0 → tabpy-2.13.0}/.scrutinizer.yml +0 -0
  24. {tabpy-2.12.0 → tabpy-2.13.0}/CONTRIBUTING.md +0 -0
  25. {tabpy-2.12.0 → tabpy-2.13.0}/Dockerfile +0 -0
  26. {tabpy-2.12.0 → tabpy-2.13.0}/LICENSE +0 -0
  27. {tabpy-2.12.0 → tabpy-2.13.0}/MANIFEST.in +0 -0
  28. {tabpy-2.12.0 → tabpy-2.13.0}/Procfile +0 -0
  29. {tabpy-2.12.0 → tabpy-2.13.0}/README.md +0 -0
  30. {tabpy-2.12.0 → tabpy-2.13.0}/_config.yml +0 -0
  31. {tabpy-2.12.0 → tabpy-2.13.0}/app.json +0 -0
  32. {tabpy-2.12.0 → tabpy-2.13.0}/codeql-analysis.yml +0 -0
  33. {tabpy-2.12.0 → tabpy-2.13.0}/docs/TableauConfiguration.md +0 -0
  34. {tabpy-2.12.0 → tabpy-2.13.0}/docs/about.md +0 -0
  35. {tabpy-2.12.0 → tabpy-2.13.0}/docs/deploy-to-heroku.md +0 -0
  36. {tabpy-2.12.0 → tabpy-2.13.0}/docs/img/Example1-SimpleFunctionCall.png +0 -0
  37. {tabpy-2.12.0 → tabpy-2.13.0}/docs/img/Example2-MultipleFunctionCalls.png +0 -0
  38. {tabpy-2.12.0 → tabpy-2.13.0}/docs/img/python-calculated-field.png +0 -0
  39. {tabpy-2.12.0 → tabpy-2.13.0}/docs/security.md +0 -0
  40. {tabpy-2.12.0 → tabpy-2.13.0}/docs/server-config.md +0 -0
  41. {tabpy-2.12.0 → tabpy-2.13.0}/docs/server-install.md +0 -0
  42. {tabpy-2.12.0 → tabpy-2.13.0}/docs/server-rest.md +0 -0
  43. {tabpy-2.12.0 → tabpy-2.13.0}/docs/tabpy-virtualenv.md +0 -0
  44. {tabpy-2.12.0 → tabpy-2.13.0}/misc/TabPy.postman_collection.json +0 -0
  45. {tabpy-2.12.0 → tabpy-2.13.0}/misc/TabPy.yml +0 -0
  46. {tabpy-2.12.0 → tabpy-2.13.0}/requirements.txt +0 -0
  47. {tabpy-2.12.0 → tabpy-2.13.0}/requirements_dev.txt +0 -0
  48. {tabpy-2.12.0 → tabpy-2.13.0}/setup.cfg +0 -0
  49. {tabpy-2.12.0 → tabpy-2.13.0}/setup.py +0 -0
  50. {tabpy-2.12.0 → tabpy-2.13.0}/start.sh +0 -0
  51. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/__init__.py +0 -0
  52. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/__init__.py +0 -0
  53. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/deploy_models.py +0 -0
  54. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/scripts/ANOVA.py +0 -0
  55. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/scripts/PCA.py +0 -0
  56. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/scripts/SentimentAnalysis.py +0 -0
  57. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/scripts/__init__.py +0 -0
  58. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/scripts/tTest.py +0 -0
  59. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/utils/__init__.py +0 -0
  60. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/models/utils/setup_utils.py +0 -0
  61. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy.py +0 -0
  62. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/__init__.py +0 -0
  63. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/app/__init__.py +0 -0
  64. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/app/app.py +0 -0
  65. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/app/app_parameters.py +0 -0
  66. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/app/arrow_server.py +0 -0
  67. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/app/util.py +0 -0
  68. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/common/__init__.py +0 -0
  69. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/common/default.conf +0 -0
  70. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/common/endpoint_file_mgr.py +0 -0
  71. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/common/messages.py +0 -0
  72. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/common/util.py +0 -0
  73. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/__init__.py +0 -0
  74. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/base_handler.py +0 -0
  75. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/basic_auth_server_middleware_factory.py +0 -0
  76. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/endpoints_handler.py +0 -0
  77. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/evaluation_plane_handler.py +0 -0
  78. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/management_handler.py +0 -0
  79. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/no_op_auth_handler.py +0 -0
  80. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/query_plane_handler.py +0 -0
  81. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/service_info_handler.py +0 -0
  82. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/status_handler.py +0 -0
  83. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/upload_destination_handler.py +0 -0
  84. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/handlers/util.py +0 -0
  85. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/management/__init__.py +0 -0
  86. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/management/util.py +0 -0
  87. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/psws/__init__.py +0 -0
  88. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/psws/callbacks.py +0 -0
  89. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/psws/python_service.py +0 -0
  90. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/state.ini.template +0 -0
  91. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/static/TabPy_logo.png +0 -0
  92. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/static/index.html +0 -0
  93. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_server/static/tableau.png +0 -0
  94. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/__init__.py +0 -0
  95. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/rest.py +0 -0
  96. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/tabpy_tools/schema.py +0 -0
  97. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/utils/__init__.py +0 -0
  98. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy/utils/tabpy_user.py +0 -0
  99. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/SOURCES.txt +0 -0
  100. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/dependency_links.txt +0 -0
  101. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/entry_points.txt +0 -0
  102. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/requires.txt +0 -0
  103. {tabpy-2.12.0 → tabpy-2.13.0}/tabpy.egg-info/top_level.txt +0 -0
  104. {tabpy-2.12.0 → tabpy-2.13.0}/tests/__init__.py +0 -0
  105. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/__init__.py +0 -0
  106. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/integ_test_base.py +0 -0
  107. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/2019_04_24_to_3018_08_25.crt +0 -0
  108. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/2019_04_24_to_3018_08_25.key +0 -0
  109. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/data.csv +0 -0
  110. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/deploy_and_evaluate_model.conf +0 -0
  111. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/deploy_and_evaluate_model_auth.conf +0 -0
  112. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/resources/pwdfile.txt +0 -0
  113. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_arrow_server.py +0 -0
  114. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_auth.py +0 -0
  115. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_custom_evaluate_timeout.py +0 -0
  116. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_and_evaluate_model.py +0 -0
  117. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_and_evaluate_model_auth_on.py +0 -0
  118. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_and_evaluate_model_ssl.py +0 -0
  119. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_model_ssl_off_auth_off.py +0 -0
  120. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_model_ssl_off_auth_on.py +0 -0
  121. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_model_ssl_on_auth_off.py +0 -0
  122. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_deploy_model_ssl_on_auth_on.py +0 -0
  123. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_evaluate.py +0 -0
  124. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_gzip.py +0 -0
  125. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_minimum_tls_version.py +0 -0
  126. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_url.py +0 -0
  127. {tabpy-2.12.0 → tabpy-2.13.0}/tests/integration/test_url_ssl.py +0 -0
  128. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/__init__.py +0 -0
  129. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/__init__.py +0 -0
  130. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/resources/expired.crt +0 -0
  131. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/resources/future.crt +0 -0
  132. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/resources/valid.crt +0 -0
  133. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_config.py +0 -0
  134. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_endpoint_file_manager.py +0 -0
  135. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_endpoint_handler.py +0 -0
  136. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_endpoints_handler.py +0 -0
  137. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_evaluation_plane_handler.py +0 -0
  138. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_pwd_file.py +0 -0
  139. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/server_tests/test_service_info_handler.py +0 -0
  140. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/tools_tests/__init__.py +0 -0
  141. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/tools_tests/test_rest.py +0 -0
  142. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/tools_tests/test_rest_object.py +0 -0
  143. {tabpy-2.12.0 → tabpy-2.13.0}/tests/unit/tools_tests/test_schema.py +0 -0
@@ -128,4 +128,4 @@ tabpy/tabpy_server/staging
128
128
  # etc
129
129
  setup.bat
130
130
  *~
131
- tabpy_log.log.1
131
+ tabpy_log.log.*
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.13.0
4
+
5
+ ### Improvements
6
+
7
+ - Add support for deploying functions to a remote TabPy server by setting
8
+ `remote_server=True` when creating the Client instance.
9
+
3
10
  ## v2.12.0
4
11
 
5
12
  ### Improvements
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tabpy
3
- Version: 2.12.0
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
- delete_path = get_query_object_path(
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(delete_path)
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
- doc_string : str, optional
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
- doc_string : str, optional
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 = self._check_and_set_is_public(is_public, endpoint_info["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__(self, endpoint, query_timeout=1000):
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 get_doc_string(self):
74
+ def get_docstring(self):
73
75
  """Get doc string from customized query"""
74
- if self.custom_query.__doc__ is not None:
75
- return self.custom_query.__doc__
76
- else:
77
- return "-- no docstring found in query function --"
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
- * get_doc_string() -- returns documentation for the Query Object
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 get_doc_string(self):
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.12.0
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)
@@ -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