tabpy 2.9.0__tar.gz → 2.10.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.9.0 → tabpy-2.10.0}/.github/workflows/pull_request.yml +3 -2
  2. {tabpy-2.9.0 → tabpy-2.10.0}/.github/workflows/push.yml +4 -3
  3. {tabpy-2.9.0 → tabpy-2.10.0}/.scrutinizer.yml +7 -1
  4. {tabpy-2.9.0 → tabpy-2.10.0}/CHANGELOG +8 -0
  5. {tabpy-2.9.0/tabpy.egg-info → tabpy-2.10.0}/PKG-INFO +34 -1
  6. {tabpy-2.9.0 → tabpy-2.10.0}/README.md +12 -0
  7. {tabpy-2.9.0 → tabpy-2.10.0}/docs/server-config.md +8 -4
  8. {tabpy-2.9.0 → tabpy-2.10.0}/docs/server-install.md +11 -1
  9. tabpy-2.10.0/tabpy/VERSION +1 -0
  10. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/app/app.py +22 -4
  11. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/app/app_parameters.py +2 -0
  12. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/app/util.py +1 -1
  13. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/common/default.conf +1 -0
  14. {tabpy-2.9.0 → tabpy-2.10.0/tabpy.egg-info}/PKG-INFO +34 -1
  15. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy.egg-info/SOURCES.txt +1 -0
  16. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/integ_test_base.py +2 -1
  17. tabpy-2.10.0/tests/integration/test_minimum_tls_version.py +54 -0
  18. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/test_config.py +14 -0
  19. tabpy-2.9.0/tabpy/VERSION +0 -1
  20. {tabpy-2.9.0 → tabpy-2.10.0}/.github/ISSUE_TEMPLATE/tabpy-issue-report.md +0 -0
  21. {tabpy-2.9.0 → tabpy-2.10.0}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
  22. {tabpy-2.9.0 → tabpy-2.10.0}/.github/workflows/coverage.yml +0 -0
  23. {tabpy-2.9.0 → tabpy-2.10.0}/.github/workflows/docker-publish.yml +0 -0
  24. {tabpy-2.9.0 → tabpy-2.10.0}/.github/workflows/lint.yml +0 -0
  25. {tabpy-2.9.0 → tabpy-2.10.0}/.gitignore +0 -0
  26. {tabpy-2.9.0 → tabpy-2.10.0}/.pep8speaks.yml +0 -0
  27. {tabpy-2.9.0 → tabpy-2.10.0}/CONTRIBUTING.md +0 -0
  28. {tabpy-2.9.0 → tabpy-2.10.0}/Dockerfile +0 -0
  29. {tabpy-2.9.0 → tabpy-2.10.0}/LICENSE +0 -0
  30. {tabpy-2.9.0 → tabpy-2.10.0}/MANIFEST.in +0 -0
  31. {tabpy-2.9.0 → tabpy-2.10.0}/Procfile +0 -0
  32. {tabpy-2.9.0 → tabpy-2.10.0}/_config.yml +0 -0
  33. {tabpy-2.9.0 → tabpy-2.10.0}/app.json +0 -0
  34. {tabpy-2.9.0 → tabpy-2.10.0}/codeql-analysis.yml +0 -0
  35. {tabpy-2.9.0 → tabpy-2.10.0}/docs/TableauConfiguration.md +0 -0
  36. {tabpy-2.9.0 → tabpy-2.10.0}/docs/about.md +0 -0
  37. {tabpy-2.9.0 → tabpy-2.10.0}/docs/deploy-to-heroku.md +0 -0
  38. {tabpy-2.9.0 → tabpy-2.10.0}/docs/img/Example1-SimpleFunctionCall.png +0 -0
  39. {tabpy-2.9.0 → tabpy-2.10.0}/docs/img/Example2-MultipleFunctionCalls.png +0 -0
  40. {tabpy-2.9.0 → tabpy-2.10.0}/docs/img/python-calculated-field.png +0 -0
  41. {tabpy-2.9.0 → tabpy-2.10.0}/docs/security.md +0 -0
  42. {tabpy-2.9.0 → tabpy-2.10.0}/docs/server-rest.md +0 -0
  43. {tabpy-2.9.0 → tabpy-2.10.0}/docs/tabpy-tools.md +0 -0
  44. {tabpy-2.9.0 → tabpy-2.10.0}/docs/tabpy-virtualenv.md +0 -0
  45. {tabpy-2.9.0 → tabpy-2.10.0}/misc/TabPy.postman_collection.json +0 -0
  46. {tabpy-2.9.0 → tabpy-2.10.0}/misc/TabPy.yml +0 -0
  47. {tabpy-2.9.0 → tabpy-2.10.0}/requirements.txt +0 -0
  48. {tabpy-2.9.0 → tabpy-2.10.0}/requirements_dev.txt +0 -0
  49. {tabpy-2.9.0 → tabpy-2.10.0}/setup.cfg +0 -0
  50. {tabpy-2.9.0 → tabpy-2.10.0}/setup.py +0 -0
  51. {tabpy-2.9.0 → tabpy-2.10.0}/start.sh +0 -0
  52. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/__init__.py +0 -0
  53. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/models/__init__.py +0 -0
  54. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/models/deploy_models.py +0 -0
  55. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/models/scripts/ANOVA.py +0 -0
  56. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/models/scripts/PCA.py +0 -0
  57. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/models/scripts/SentimentAnalysis.py +0 -0
  58. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/models/scripts/__init__.py +0 -0
  59. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/models/scripts/tTest.py +0 -0
  60. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/models/utils/__init__.py +0 -0
  61. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/models/utils/setup_utils.py +0 -0
  62. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy.py +0 -0
  63. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/__init__.py +0 -0
  64. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/app/__init__.py +0 -0
  65. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/app/arrow_server.py +0 -0
  66. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/common/__init__.py +0 -0
  67. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/common/endpoint_file_mgr.py +0 -0
  68. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/common/messages.py +0 -0
  69. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/common/util.py +0 -0
  70. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/__init__.py +0 -0
  71. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/base_handler.py +0 -0
  72. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/basic_auth_server_middleware_factory.py +0 -0
  73. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/endpoint_handler.py +0 -0
  74. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/endpoints_handler.py +0 -0
  75. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/evaluation_plane_handler.py +0 -0
  76. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/management_handler.py +0 -0
  77. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/no_op_auth_handler.py +0 -0
  78. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/query_plane_handler.py +0 -0
  79. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/service_info_handler.py +0 -0
  80. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/status_handler.py +0 -0
  81. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/upload_destination_handler.py +0 -0
  82. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/handlers/util.py +0 -0
  83. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/management/__init__.py +0 -0
  84. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/management/state.py +0 -0
  85. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/management/util.py +0 -0
  86. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/psws/__init__.py +0 -0
  87. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/psws/callbacks.py +0 -0
  88. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/psws/python_service.py +0 -0
  89. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/state.ini.template +0 -0
  90. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/static/TabPy_logo.png +0 -0
  91. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/static/index.html +0 -0
  92. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_server/static/tableau.png +0 -0
  93. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_tools/__init__.py +0 -0
  94. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_tools/client.py +0 -0
  95. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_tools/custom_query_object.py +0 -0
  96. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_tools/query_object.py +0 -0
  97. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_tools/rest.py +0 -0
  98. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_tools/rest_client.py +0 -0
  99. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/tabpy_tools/schema.py +0 -0
  100. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/utils/__init__.py +0 -0
  101. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy/utils/tabpy_user.py +0 -0
  102. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy.egg-info/dependency_links.txt +0 -0
  103. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy.egg-info/entry_points.txt +0 -0
  104. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy.egg-info/requires.txt +0 -0
  105. {tabpy-2.9.0 → tabpy-2.10.0}/tabpy.egg-info/top_level.txt +0 -0
  106. {tabpy-2.9.0 → tabpy-2.10.0}/tests/__init__.py +0 -0
  107. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/__init__.py +0 -0
  108. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/resources/2019_04_24_to_3018_08_25.crt +0 -0
  109. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/resources/2019_04_24_to_3018_08_25.key +0 -0
  110. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/resources/data.csv +0 -0
  111. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/resources/deploy_and_evaluate_model.conf +0 -0
  112. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/resources/deploy_and_evaluate_model_auth.conf +0 -0
  113. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/resources/pwdfile.txt +0 -0
  114. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_arrow_server.py +0 -0
  115. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_auth.py +0 -0
  116. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_custom_evaluate_timeout.py +0 -0
  117. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_deploy_and_evaluate_model.py +0 -0
  118. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_deploy_and_evaluate_model_auth_on.py +0 -0
  119. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_deploy_and_evaluate_model_ssl.py +0 -0
  120. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_deploy_model_ssl_off_auth_off.py +0 -0
  121. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_deploy_model_ssl_off_auth_on.py +0 -0
  122. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_deploy_model_ssl_on_auth_off.py +0 -0
  123. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_deploy_model_ssl_on_auth_on.py +0 -0
  124. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_evaluate.py +0 -0
  125. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_gzip.py +0 -0
  126. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_url.py +0 -0
  127. {tabpy-2.9.0 → tabpy-2.10.0}/tests/integration/test_url_ssl.py +0 -0
  128. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/__init__.py +0 -0
  129. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/__init__.py +0 -0
  130. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/resources/expired.crt +0 -0
  131. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/resources/future.crt +0 -0
  132. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/resources/valid.crt +0 -0
  133. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/test_endpoint_file_manager.py +0 -0
  134. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/test_endpoint_handler.py +0 -0
  135. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/test_endpoints_handler.py +0 -0
  136. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/test_evaluation_plane_handler.py +0 -0
  137. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/test_pwd_file.py +0 -0
  138. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/server_tests/test_service_info_handler.py +0 -0
  139. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/tools_tests/__init__.py +0 -0
  140. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/tools_tests/test_client.py +0 -0
  141. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/tools_tests/test_rest.py +0 -0
  142. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/tools_tests/test_rest_object.py +0 -0
  143. {tabpy-2.9.0 → tabpy-2.10.0}/tests/unit/tools_tests/test_schema.py +0 -0
@@ -11,8 +11,9 @@ jobs:
11
11
  matrix:
12
12
  # TODO: Add 3.7 to python-versions after GitHub action regression is resolved.
13
13
  # https://github.com/actions/setup-python/issues/682
14
- python-version: [3.8, 3.9]
15
- os: [ubuntu-latest, windows-latest, macos-latest]
14
+ # TODO: switch macos-13 to macos-latest@arm64
15
+ python-version: ['3.8', '3.9', '3.10']
16
+ os: [ubuntu-latest, windows-latest, macos-13]
16
17
 
17
18
  steps:
18
19
  - uses: actions/checkout@v1
@@ -11,9 +11,10 @@ jobs:
11
11
  matrix:
12
12
  # TODO: Add 3.7 to python-versions after GitHub action regression is resolved.
13
13
  # https://github.com/actions/setup-python/issues/682
14
- python-version: [3.8, 3.9]
15
- os: [ubuntu-latest, windows-latest, macos-latest]
16
-
14
+ # TODO: switch macos-13 to macos-latest@arm64
15
+ python-version: ['3.8', '3.9', '3.10']
16
+ os: [ubuntu-latest, windows-latest, macos-13]
17
+
17
18
  steps:
18
19
  - uses: actions/checkout@v1
19
20
 
@@ -10,6 +10,7 @@ build:
10
10
  tests:
11
11
  override:
12
12
  - command: 'pytest tests --cov=tabpy --cov-config=setup.cfg'
13
+ idle_timeout: 600
13
14
  coverage:
14
15
  file: '.coverage'
15
16
  config_file: 'setup.cfg'
@@ -20,10 +21,15 @@ build:
20
21
  - pip install -r requirements.txt
21
22
  tests:
22
23
  override: [py-scrutinizer-run]
24
+ dependencies:
25
+ override:
26
+ - pip install .
23
27
  tests:
24
28
  before:
25
29
  - pip install -r requirements.txt
26
- override: [pytest]
30
+ override:
31
+ pytest:
32
+ idle_timeout: 600
27
33
  checks:
28
34
  python:
29
35
  code_rating: true
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## v2.10.0
4
+
5
+ ### Improvements
6
+
7
+ - Add TabPy parameter (TABPY_MINIMUM_TLS_VERSION) to specify the minimum TLS
8
+ version that the server will accept for secure connections. Default is
9
+ set to TLSv1_2.
10
+
3
11
  ## v2.9.0
4
12
 
5
13
  ### Improvements
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tabpy
3
- Version: 2.9.0
3
+ Version: 2.10.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
@@ -33,6 +33,31 @@ Classifier: Operating System :: MacOS
33
33
  Requires-Python: >=3.7
34
34
  Description-Content-Type: text/markdown
35
35
  License-File: LICENSE
36
+ Requires-Dist: cloudpickle
37
+ Requires-Dist: configparser
38
+ Requires-Dist: coverage
39
+ Requires-Dist: coveralls
40
+ Requires-Dist: docopt
41
+ Requires-Dist: future
42
+ Requires-Dist: genson
43
+ Requires-Dist: hypothesis
44
+ Requires-Dist: jsonschema
45
+ Requires-Dist: mock
46
+ Requires-Dist: nltk
47
+ Requires-Dist: numpy
48
+ Requires-Dist: pandas
49
+ Requires-Dist: pyopenssl
50
+ Requires-Dist: pytest
51
+ Requires-Dist: pytest-cov
52
+ Requires-Dist: requests
53
+ Requires-Dist: scipy
54
+ Requires-Dist: simplejson
55
+ Requires-Dist: scikit-learn
56
+ Requires-Dist: textblob
57
+ Requires-Dist: tornado
58
+ Requires-Dist: twisted
59
+ Requires-Dist: urllib3
60
+ Requires-Dist: pyarrow
36
61
 
37
62
 
38
63
  TabPy (the Tableau Python Server) is an external service implementation
@@ -41,6 +66,14 @@ scripts and saved functions via Tableau's table calculations.
41
66
 
42
67
  # Changelog
43
68
 
69
+ ## v2.10.0
70
+
71
+ ### Improvements
72
+
73
+ - Add TabPy parameter (TABPY_MINIMUM_TLS_VERSION) to specify the minimum TLS
74
+ version that the server will accept for secure connections. Default is
75
+ set to TLSv1_2.
76
+
44
77
  ## v2.9.0
45
78
 
46
79
  ### Improvements
@@ -27,6 +27,18 @@ Consider reading TabPy documentation in the following order:
27
27
  * [Authoring Python calculations in Tableau](docs/TableauConfiguration.md).
28
28
  * [TabPy Tools](docs/tabpy-tools.md)
29
29
 
30
+ Important security note:
31
+
32
+ * By default, TabPy is configured without username/password authentication.
33
+ We strongly advise using TabPy only with authentication enabled. For more
34
+ information, see
35
+ [TabPy Server Configuration Instructions](docs/server-config.md#authentication).
36
+ Without authentication in place, if the TABPY_EVALUATE_ENABLE feature is
37
+ enabled (as it is by default), there is the possibility that unauthenticated
38
+ individuals could remotely execute code on the machine running TabPy.
39
+ Leaving these two settings in their default states together is highly
40
+ discouraged.
41
+
30
42
  Troubleshooting:
31
43
 
32
44
  * [TabPy Wiki](https://github.com/tableau/TabPy/wiki)
@@ -8,7 +8,6 @@
8
8
  * [Configuration File Content](#configuration-file-content)
9
9
  * [Configuration File Example](#configuration-file-example)
10
10
  - [Configuring HTTP vs HTTPS](#configuring-http-vs-https)
11
- - [Configuring TPS](#configuring-http-vs-https)
12
11
  - [Authentication](#authentication)
13
12
  * [Enabling Authentication](#enabling-authentication)
14
13
  * [Password File](#password-file)
@@ -83,6 +82,10 @@ at [`logging.config` documentation page](https://docs.python.org/3.6/library/log
83
82
  - `TABPY_KEY_FILE` - absolute path to private key file to run TabPy with.
84
83
  Only used with `TABPY_TRANSFER_PROTOCOL` set to `https`. Default value -
85
84
  not set.
85
+ - `TABPY_MINIMUM_TLS_VERSION` - set the minimum TLS version that the server
86
+ will accept for secure connections (`TLSv1_2`, `TLSv1_3`, etc). Refer to
87
+ [docs.python.org](https://docs.python.org/3/library/ssl.html#ssl.TLSVersion.MINIMUM_SUPPORTED)
88
+ for acceptable values. Default value - `TLSv1_2`.
86
89
  - `TABPY_LOG_DETAILS` - when set to `true` additional call information
87
90
  (caller IP, URL, client info, etc.) is logged. Default value - `false`.
88
91
  - `TABPY_MAX_REQUEST_SIZE_MB` - maximal request size supported by TabPy server
@@ -124,6 +127,7 @@ settings._
124
127
  # TABPY_TRANSFER_PROTOCOL = https
125
128
  # TABPY_CERTIFICATE_FILE = /path/to/certificate/file.crt
126
129
  # TABPY_KEY_FILE = /path/to/key/file.key
130
+ # TABPY_MINIMUM_TLS_VERSION = TLSv1_2
127
131
 
128
132
  # Log additional request details including caller IP, full URL, client
129
133
  # end user info if provided.
@@ -314,14 +318,14 @@ For extended logging (e.g. for auditing purposes) additional logging can be turn
314
318
  on with setting `TABPY_LOG_DETAILS` configuration file parameter to `true`.
315
319
 
316
320
  With the feature on additional information is logged for HTTP requests: caller ip,
317
- URL, client infomation (Tableau Desktop\Server), Tableau user name (for Tableau Server)
318
- and TabPy user name as shown in the example below:
321
+ URL, client infomation (Tableau Desktop\Server) and TabPy user name as shown in
322
+ the example below:
319
323
 
320
324
  <!-- markdownlint-disable MD013 -->
321
325
  <!-- markdownlint-disable MD040 -->
322
326
 
323
327
  ```
324
- 2019-05-02,13:50:08 [INFO] (base_handler.py:base_handler:90): Call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4, Caller: ::1, Method: POST, Resource: http://localhost:9004/evaluate, Client: Postman for manual testing, Tableau user: ogolovatyi
328
+ 2019-05-02,13:50:08 [INFO] (base_handler.py:base_handler:90): Call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4, Caller: ::1, Method: POST, Resource: http://localhost:9004/evaluate, Client: Postman for manual testing
325
329
  2019-05-02,13:50:08 [DEBUG] (base_handler.py:base_handler:120): Checking if need to handle authentication, <<
326
330
  call ID: 934073bd-0d29-46d3-b693-b1e4b1efa9e4>>
327
331
  2019-05-02,13:50:08 [DEBUG] (base_handler.py:base_handler:120): Handling authentication, <<call ID: 934073bd-
@@ -15,6 +15,16 @@ These instructions explain how to install and start up TabPy Server.
15
15
 
16
16
  ## TabPy Installation
17
17
 
18
+ ### Prerequisites
19
+
20
+ - TabPy only supports Python versions 3.7, 3.8, 3.9, and 3.10
21
+ - TabPy 2.7.0 added support for data streaming via
22
+ Apache Arrow Flight (`pyarrow`). When using TabPy >= 2.7.0, we
23
+ recommend using a Python 64-bit installer to avoid incompatibility issues
24
+ related to `pyarrow`.
25
+
26
+ ### Installation
27
+
18
28
  To install TabPy on to an environment `pip` needs to be installed and
19
29
  updated first:
20
30
 
@@ -30,7 +40,7 @@ pip install tabpy
30
40
 
31
41
  ## Starting TabPy
32
42
 
33
- To start TabPy with default setting run the following command:
43
+ To start TabPy with default settings run the following command:
34
44
 
35
45
  ```sh
36
46
  tabpy
@@ -0,0 +1 @@
1
+ 2.10.0
@@ -5,6 +5,7 @@ import multiprocessing
5
5
  import os
6
6
  import shutil
7
7
  import signal
8
+ import ssl
8
9
  import sys
9
10
  import _thread
10
11
 
@@ -83,6 +84,24 @@ class TabPyApp:
83
84
 
84
85
  self._parse_config(config_file)
85
86
 
87
+ def _initialize_ssl_context(self):
88
+ ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
89
+
90
+ ssl_context.load_cert_chain(
91
+ certfile=self.settings[SettingsParameters.CertificateFile],
92
+ keyfile=self.settings[SettingsParameters.KeyFile]
93
+ )
94
+
95
+ min_tls = self.settings[SettingsParameters.MinimumTLSVersion]
96
+ if not hasattr(ssl.TLSVersion, min_tls):
97
+ logger.warning(f"Unrecognized value for TABPY_MINIMUM_TLS_VERSION: {min_tls}")
98
+ min_tls = "TLSv1_2"
99
+
100
+ logger.info(f"Setting minimum TLS version to {min_tls}")
101
+ ssl_context.minimum_version = ssl.TLSVersion[min_tls]
102
+
103
+ return ssl_context
104
+
86
105
  def _get_tls_certificates(self, config):
87
106
  tls_certificates = []
88
107
  cert = config[SettingsParameters.CertificateFile]
@@ -127,10 +146,7 @@ class TabPyApp:
127
146
  protocol = self.settings[SettingsParameters.TransferProtocol]
128
147
  ssl_options = None
129
148
  if protocol == "https":
130
- ssl_options = {
131
- "certfile": self.settings[SettingsParameters.CertificateFile],
132
- "keyfile": self.settings[SettingsParameters.KeyFile],
133
- }
149
+ ssl_options = self._initialize_ssl_context()
134
150
  elif protocol != "http":
135
151
  msg = f"Unsupported transfer protocol {protocol}."
136
152
  logger.critical(msg)
@@ -328,6 +344,8 @@ class TabPyApp:
328
344
  (SettingsParameters.CertificateFile, ConfigParameters.TABPY_CERTIFICATE_FILE,
329
345
  None, None),
330
346
  (SettingsParameters.KeyFile, ConfigParameters.TABPY_KEY_FILE, None, None),
347
+ (SettingsParameters.MinimumTLSVersion, ConfigParameters.TABPY_MINIMUM_TLS_VERSION,
348
+ "TLSv1_2", None),
331
349
  (SettingsParameters.StateFilePath, ConfigParameters.TABPY_STATE_PATH,
332
350
  os.path.join(pkg_path, "tabpy_server"), None),
333
351
  (SettingsParameters.StaticPath, ConfigParameters.TABPY_STATIC_PATH,
@@ -10,6 +10,7 @@ class ConfigParameters:
10
10
  TABPY_TRANSFER_PROTOCOL = "TABPY_TRANSFER_PROTOCOL"
11
11
  TABPY_CERTIFICATE_FILE = "TABPY_CERTIFICATE_FILE"
12
12
  TABPY_KEY_FILE = "TABPY_KEY_FILE"
13
+ TABPY_MINIMUM_TLS_VERSION = "TABPY_MINIMUM_TLS_VERSION"
13
14
  TABPY_LOG_DETAILS = "TABPY_LOG_DETAILS"
14
15
  TABPY_STATIC_PATH = "TABPY_STATIC_PATH"
15
16
  TABPY_MAX_REQUEST_SIZE_MB = "TABPY_MAX_REQUEST_SIZE_MB"
@@ -33,6 +34,7 @@ class SettingsParameters:
33
34
  UploadDir = "upload_dir"
34
35
  CertificateFile = "certificate_file"
35
36
  KeyFile = "key_file"
37
+ MinimumTLSVersion = "minimum_tls_version"
36
38
  StateFilePath = "state_file_path"
37
39
  ApiVersions = "versions"
38
40
  LogRequestContext = "log_request_context"
@@ -17,7 +17,7 @@ def validate_cert(cert_file_path):
17
17
  date_format, encoding = "%Y%m%d%H%M%SZ", "ascii"
18
18
  not_before = datetime.strptime(cert.get_notBefore().decode(encoding), date_format)
19
19
  not_after = datetime.strptime(cert.get_notAfter().decode(encoding), date_format)
20
- now = datetime.now()
20
+ now = datetime.utcnow()
21
21
 
22
22
  https_error = "Error using HTTPS: "
23
23
  if now < not_before:
@@ -15,6 +15,7 @@
15
15
  # TABPY_TRANSFER_PROTOCOL = https
16
16
  # TABPY_CERTIFICATE_FILE = /path/to/certificate/file.crt
17
17
  # TABPY_KEY_FILE = /path/to/key/file.key
18
+ # TABPY_MINIMUM_TLS_VERSION = TLSv1_2
18
19
 
19
20
  # Log additional request details including caller IP, full URL, client
20
21
  # end user info if provided.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tabpy
3
- Version: 2.9.0
3
+ Version: 2.10.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
@@ -33,6 +33,31 @@ Classifier: Operating System :: MacOS
33
33
  Requires-Python: >=3.7
34
34
  Description-Content-Type: text/markdown
35
35
  License-File: LICENSE
36
+ Requires-Dist: cloudpickle
37
+ Requires-Dist: configparser
38
+ Requires-Dist: coverage
39
+ Requires-Dist: coveralls
40
+ Requires-Dist: docopt
41
+ Requires-Dist: future
42
+ Requires-Dist: genson
43
+ Requires-Dist: hypothesis
44
+ Requires-Dist: jsonschema
45
+ Requires-Dist: mock
46
+ Requires-Dist: nltk
47
+ Requires-Dist: numpy
48
+ Requires-Dist: pandas
49
+ Requires-Dist: pyopenssl
50
+ Requires-Dist: pytest
51
+ Requires-Dist: pytest-cov
52
+ Requires-Dist: requests
53
+ Requires-Dist: scipy
54
+ Requires-Dist: simplejson
55
+ Requires-Dist: scikit-learn
56
+ Requires-Dist: textblob
57
+ Requires-Dist: tornado
58
+ Requires-Dist: twisted
59
+ Requires-Dist: urllib3
60
+ Requires-Dist: pyarrow
36
61
 
37
62
 
38
63
  TabPy (the Tableau Python Server) is an external service implementation
@@ -41,6 +66,14 @@ scripts and saved functions via Tableau's table calculations.
41
66
 
42
67
  # Changelog
43
68
 
69
+ ## v2.10.0
70
+
71
+ ### Improvements
72
+
73
+ - Add TabPy parameter (TABPY_MINIMUM_TLS_VERSION) to specify the minimum TLS
74
+ version that the server will accept for secure connections. Default is
75
+ set to TLSv1_2.
76
+
44
77
  ## v2.9.0
45
78
 
46
79
  ### Improvements
@@ -113,6 +113,7 @@ tests/integration/test_deploy_model_ssl_on_auth_off.py
113
113
  tests/integration/test_deploy_model_ssl_on_auth_on.py
114
114
  tests/integration/test_evaluate.py
115
115
  tests/integration/test_gzip.py
116
+ tests/integration/test_minimum_tls_version.py
116
117
  tests/integration/test_url.py
117
118
  tests/integration/test_url_ssl.py
118
119
  tests/integration/resources/2019_04_24_to_3018_08_25.crt
@@ -225,7 +225,8 @@ class IntegTestBase(unittest.TestCase):
225
225
 
226
226
  # Platform specific - for integration tests we want to engage
227
227
  # startup script
228
- with open(self.tmp_dir + "/output.txt", "w") as outfile:
228
+ self.log_file_path = os.path.join(self.tmp_dir, "output.txt")
229
+ with open(self.log_file_path, "w") as outfile:
229
230
  cmd = ["tabpy", "--config=" + self.config_file_name, "--disable-auth-warning"]
230
231
  preexec_fn = None
231
232
  if platform.system() == "Windows":
@@ -0,0 +1,54 @@
1
+ from . import integ_test_base
2
+ import os
3
+
4
+ class TestMinimumTLSVersion(integ_test_base.IntegTestBase):
5
+ def _get_log_contents(self):
6
+ with open(self.log_file_path, 'r') as f:
7
+ return f.read()
8
+
9
+ def _get_config_file_name(self, tls_version: str) -> str:
10
+ config_file = open(os.path.join(self.tmp_dir, "test.conf"), "w+")
11
+ config_file.write(
12
+ "[TabPy]\n"
13
+ "TABPY_PORT = 9005\n"
14
+ "TABPY_TRANSFER_PROTOCOL = https\n"
15
+ "TABPY_CERTIFICATE_FILE = ./tests/integration/resources/2019_04_24_to_3018_08_25.crt\n"
16
+ "TABPY_KEY_FILE = ./tests/integration/resources/2019_04_24_to_3018_08_25.key\n"
17
+ )
18
+
19
+ if tls_version is not None:
20
+ config_file.write(f"TABPY_MINIMUM_TLS_VERSION = {tls_version}")
21
+
22
+ pwd_file = self._get_pwd_file()
23
+ if pwd_file is not None:
24
+ pwd_file = os.path.abspath(pwd_file)
25
+ config_file.write(f"TABPY_PWD_FILE = {pwd_file}\n")
26
+
27
+ config_file.close()
28
+ self.delete_config_file = True
29
+ return config_file.name
30
+
31
+ class TestMinimumTLSVersionValid(TestMinimumTLSVersion):
32
+ def _get_config_file_name(self) -> str:
33
+ return super()._get_config_file_name("TLSv1_3")
34
+
35
+ def test_minimum_tls_version_valid(self):
36
+ log_contents = self._get_log_contents()
37
+ self.assertIn("Setting minimum TLS version to TLSv1_3", log_contents)
38
+
39
+ class TestMinimumTLSVersionInvalid(TestMinimumTLSVersion):
40
+ def _get_config_file_name(self) -> str:
41
+ return super()._get_config_file_name("TLSv-1.3")
42
+
43
+ def test_minimum_tls_version_invalid(self):
44
+ log_contents = self._get_log_contents()
45
+ self.assertIn("Unrecognized value for TABPY_MINIMUM_TLS_VERSION", log_contents)
46
+ self.assertIn("Setting minimum TLS version to TLSv1_2", log_contents)
47
+
48
+ class TestMinimumTLSVersionNotSpecified(TestMinimumTLSVersion):
49
+ def _get_config_file_name(self) -> str:
50
+ return super()._get_config_file_name(None)
51
+
52
+ def test_minimum_tls_version_not_specified(self):
53
+ log_contents = self._get_log_contents()
54
+ self.assertIn("Setting minimum TLS version to TLSv1_2", log_contents)
@@ -209,6 +209,20 @@ class TestPartialConfigFile(unittest.TestCase):
209
209
  app = TabPyApp(self.config_file.name)
210
210
  self.assertEqual(app.settings["gzip_enabled"], False)
211
211
 
212
+ @patch("tabpy.tabpy_server.app.app.os.path.exists", return_value=True)
213
+ @patch("tabpy.tabpy_server.app.app._get_state_from_file")
214
+ @patch("tabpy.tabpy_server.app.app.TabPyState")
215
+ def test_min_tls_setting_valid(
216
+ self, mock_state, mock_get_state_from_file, mock_path_exists
217
+ ):
218
+ self.assertTrue(self.config_file is not None)
219
+ config_file = self.config_file
220
+ config_file.write("[TabPy]\n" "TABPY_MINIMUM_TLS_VERSION = TLSv1_3".encode())
221
+ config_file.close()
222
+
223
+ app = TabPyApp(self.config_file.name)
224
+ self.assertEqual(app.settings["minimum_tls_version"], "TLSv1_3")
225
+
212
226
  class TestTransferProtocolValidation(unittest.TestCase):
213
227
  def assertTabPyAppRaisesRuntimeError(self, expected_message):
214
228
  with self.assertRaises(RuntimeError) as err:
tabpy-2.9.0/tabpy/VERSION DELETED
@@ -1 +0,0 @@
1
- 2.9.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