tabpy 2.10.0__tar.gz → 2.12.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.10.0 → tabpy-2.12.0}/.github/workflows/coverage.yml +1 -1
- {tabpy-2.10.0 → tabpy-2.12.0}/.github/workflows/lint.yml +1 -1
- {tabpy-2.10.0 → tabpy-2.12.0}/.github/workflows/pull_request.yml +2 -4
- {tabpy-2.10.0 → tabpy-2.12.0}/.github/workflows/push.yml +2 -4
- {tabpy-2.10.0 → tabpy-2.12.0}/.scrutinizer.yml +1 -1
- {tabpy-2.10.0 → tabpy-2.12.0}/CHANGELOG +16 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/CONTRIBUTING.md +2 -2
- {tabpy-2.10.0 → tabpy-2.12.0}/LICENSE +1 -1
- {tabpy-2.10.0 → tabpy-2.12.0}/PKG-INFO +20 -3
- {tabpy-2.10.0 → tabpy-2.12.0}/README.md +0 -1
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/server-install.md +3 -7
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/tabpy-tools.md +61 -10
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/tabpy-virtualenv.md +1 -1
- {tabpy-2.10.0 → tabpy-2.12.0}/setup.py +3 -2
- tabpy-2.12.0/tabpy/VERSION +1 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/endpoint_handler.py +4 -2
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/management_handler.py +3 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/management/state.py +12 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_tools/client.py +80 -6
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_tools/rest_client.py +12 -2
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy.egg-info/PKG-INFO +20 -3
- tabpy-2.10.0/tabpy/VERSION +0 -1
- {tabpy-2.10.0 → tabpy-2.12.0}/.github/ISSUE_TEMPLATE/tabpy-issue-report.md +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/.github/workflows/docker-publish.yml +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/.gitignore +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/.pep8speaks.yml +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/Dockerfile +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/MANIFEST.in +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/Procfile +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/_config.yml +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/app.json +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/codeql-analysis.yml +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/TableauConfiguration.md +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/about.md +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/deploy-to-heroku.md +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/img/Example1-SimpleFunctionCall.png +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/img/Example2-MultipleFunctionCalls.png +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/img/python-calculated-field.png +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/security.md +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/server-config.md +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/docs/server-rest.md +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/misc/TabPy.postman_collection.json +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/misc/TabPy.yml +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/requirements.txt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/requirements_dev.txt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/setup.cfg +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/start.sh +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/models/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/models/deploy_models.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/models/scripts/ANOVA.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/models/scripts/PCA.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/models/scripts/SentimentAnalysis.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/models/scripts/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/models/scripts/tTest.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/models/utils/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/models/utils/setup_utils.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/app/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/app/app.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/app/app_parameters.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/app/arrow_server.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/app/util.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/common/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/common/default.conf +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/common/endpoint_file_mgr.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/common/messages.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/common/util.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/base_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/basic_auth_server_middleware_factory.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/endpoints_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/evaluation_plane_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/no_op_auth_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/query_plane_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/service_info_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/status_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/upload_destination_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/handlers/util.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/management/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/management/util.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/psws/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/psws/callbacks.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/psws/python_service.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/state.ini.template +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/static/TabPy_logo.png +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/static/index.html +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_server/static/tableau.png +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_tools/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_tools/custom_query_object.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_tools/query_object.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_tools/rest.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/tabpy_tools/schema.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/utils/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy/utils/tabpy_user.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy.egg-info/SOURCES.txt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy.egg-info/dependency_links.txt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy.egg-info/entry_points.txt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy.egg-info/requires.txt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tabpy.egg-info/top_level.txt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/integ_test_base.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/resources/2019_04_24_to_3018_08_25.crt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/resources/2019_04_24_to_3018_08_25.key +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/resources/data.csv +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/resources/deploy_and_evaluate_model.conf +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/resources/deploy_and_evaluate_model_auth.conf +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/resources/pwdfile.txt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_arrow_server.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_auth.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_custom_evaluate_timeout.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_deploy_and_evaluate_model.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_deploy_and_evaluate_model_auth_on.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_deploy_and_evaluate_model_ssl.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_deploy_model_ssl_off_auth_off.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_deploy_model_ssl_off_auth_on.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_deploy_model_ssl_on_auth_off.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_deploy_model_ssl_on_auth_on.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_evaluate.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_gzip.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_minimum_tls_version.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_url.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/integration/test_url_ssl.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/resources/expired.crt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/resources/future.crt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/resources/valid.crt +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/test_config.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/test_endpoint_file_manager.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/test_endpoint_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/test_endpoints_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/test_evaluation_plane_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/test_pwd_file.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/server_tests/test_service_info_handler.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/tools_tests/__init__.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/tools_tests/test_client.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/tools_tests/test_rest.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/tools_tests/test_rest_object.py +0 -0
- {tabpy-2.10.0 → tabpy-2.12.0}/tests/unit/tools_tests/test_schema.py +0 -0
|
@@ -9,10 +9,8 @@ jobs:
|
|
|
9
9
|
|
|
10
10
|
strategy:
|
|
11
11
|
matrix:
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
# TODO: switch macos-13 to macos-latest@arm64
|
|
15
|
-
python-version: ['3.8', '3.9', '3.10']
|
|
12
|
+
python-version: ['3.9', '3.10', '3.11', '3.12']
|
|
13
|
+
# TODO: switch macos-13 to macos-latest
|
|
16
14
|
os: [ubuntu-latest, windows-latest, macos-13]
|
|
17
15
|
|
|
18
16
|
steps:
|
|
@@ -9,10 +9,8 @@ jobs:
|
|
|
9
9
|
|
|
10
10
|
strategy:
|
|
11
11
|
matrix:
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
# TODO: switch macos-13 to macos-latest@arm64
|
|
15
|
-
python-version: ['3.8', '3.9', '3.10']
|
|
12
|
+
python-version: ['3.9', '3.10', '3.11', '3.12']
|
|
13
|
+
# TODO: switch macos-13 to macos-latest
|
|
16
14
|
os: [ubuntu-latest, windows-latest, macos-13]
|
|
17
15
|
|
|
18
16
|
steps:
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2.12.0
|
|
4
|
+
|
|
5
|
+
### Improvements
|
|
6
|
+
|
|
7
|
+
- Add support for public deployed functions that will be visible to users
|
|
8
|
+
in Tableau when using the Custom Functions Explorer
|
|
9
|
+
- Add functionality to allow users to update existing deployed functions
|
|
10
|
+
without needing to redeploy the function itself
|
|
11
|
+
|
|
12
|
+
## v2.11.0
|
|
13
|
+
|
|
14
|
+
### Improvements
|
|
15
|
+
|
|
16
|
+
- Add support for Python 3.10, 3.11, and 3.12. End support Python for
|
|
17
|
+
3.7 and 3.8.
|
|
18
|
+
|
|
3
19
|
## v2.10.0
|
|
4
20
|
|
|
5
21
|
### Improvements
|
|
@@ -30,8 +30,8 @@ and run it locally.
|
|
|
30
30
|
These are prerequisites for an environment required for a contributor to
|
|
31
31
|
be able to work on TabPy changes:
|
|
32
32
|
|
|
33
|
-
-
|
|
34
|
-
|
|
33
|
+
- Supported 64-bit Python version (see
|
|
34
|
+
[README](https://github.com/tableau/TabPy) for a list of compatible versions).
|
|
35
35
|
- git
|
|
36
36
|
- Node.js for npm packages - install from <https://nodejs.org>.
|
|
37
37
|
- NPM packages - install all with
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tabpy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.12.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
|
|
@@ -21,9 +21,10 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
21
21
|
Classifier: Intended Audience :: Developers
|
|
22
22
|
Classifier: Intended Audience :: Science/Research
|
|
23
23
|
Classifier: License :: OSI Approved :: MIT License
|
|
24
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
25
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
26
24
|
Classifier: Programming Language :: Python :: 3.9
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
27
28
|
Classifier: Topic :: Scientific/Engineering
|
|
28
29
|
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
29
30
|
Classifier: Operating System :: Microsoft :: Windows
|
|
@@ -66,6 +67,22 @@ scripts and saved functions via Tableau's table calculations.
|
|
|
66
67
|
|
|
67
68
|
# Changelog
|
|
68
69
|
|
|
70
|
+
## v2.12.0
|
|
71
|
+
|
|
72
|
+
### Improvements
|
|
73
|
+
|
|
74
|
+
- Add support for public deployed functions that will be visible to users
|
|
75
|
+
in Tableau when using the Custom Functions Explorer
|
|
76
|
+
- Add functionality to allow users to update existing deployed functions
|
|
77
|
+
without needing to redeploy the function itself
|
|
78
|
+
|
|
79
|
+
## v2.11.0
|
|
80
|
+
|
|
81
|
+
### Improvements
|
|
82
|
+
|
|
83
|
+
- Add support for Python 3.10, 3.11, and 3.12. End support Python for
|
|
84
|
+
3.7 and 3.8.
|
|
85
|
+
|
|
69
86
|
## v2.10.0
|
|
70
87
|
|
|
71
88
|
### Improvements
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
[](https://github.com/tableau/TabPy/actions?query=workflow%3A%22Test+Run+on+Push%22)
|
|
7
7
|
[](https://coveralls.io/github/tableau/TabPy?branch=master)
|
|
8
8
|
[](https://scrutinizer-ci.com/g/tableau/TabPy/?branch=master)
|
|
9
|
-
[](https://requires.io/github/tableau/TabPy/requirements/?branch=master)
|
|
10
9
|
|
|
11
10
|

|
|
12
11
|
[](https://pypi.python.org/pypi/tabpy/)
|
|
@@ -15,13 +15,9 @@ These instructions explain how to install and start up TabPy Server.
|
|
|
15
15
|
|
|
16
16
|
## TabPy Installation
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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`.
|
|
18
|
+
First, ensure that you are using a supported 64-bit Python version.
|
|
19
|
+
Refer to the [README](https://github.com/tableau/TabPy) for a list
|
|
20
|
+
of compatible versions.
|
|
25
21
|
|
|
26
22
|
### Installation
|
|
27
23
|
|
|
@@ -71,6 +71,14 @@ def add(x,y):
|
|
|
71
71
|
client.deploy('add', add, 'Adds two numbers x and y')
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
If you would like your function to be visible in Tableau using the Custom Functions
|
|
75
|
+
Explorer, you will need to deploy setting the optional `is_public` parameter to True.
|
|
76
|
+
Functions that have `is_public` set to False, or unset will not be visible.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
client.deploy('add', add, 'Adds two numbers x and y', is_public=True)
|
|
80
|
+
```
|
|
81
|
+
|
|
74
82
|
The next example is more complex, using scikit-learn's clustering API:
|
|
75
83
|
|
|
76
84
|
```python
|
|
@@ -100,16 +108,6 @@ endpoint named `clustering`.
|
|
|
100
108
|
It is now reachable as a [REST API](server-rest.md#httppost-queryendpoint), as
|
|
101
109
|
well as through the TabPy tools - for details see the next section.
|
|
102
110
|
|
|
103
|
-
You can re-deploy a function (for example, after you modified its code) by setting
|
|
104
|
-
the `override` parameter to `True`:
|
|
105
|
-
|
|
106
|
-
```python
|
|
107
|
-
client.deploy('add', add, 'Adds two numbers x and y', override=True)
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Each re-deployment of an endpoint will increment its version number, which is also
|
|
111
|
-
returned as part of the query result.
|
|
112
|
-
|
|
113
111
|
When deploying endpoints which rely on supervised learning models, you may want to
|
|
114
112
|
load a saved model instead of training on-the-fly for performance reasons.
|
|
115
113
|
|
|
@@ -158,9 +156,62 @@ The endpoints that are no longer needed can be removed the following way:
|
|
|
158
156
|
```python
|
|
159
157
|
|
|
160
158
|
client.remove('WillItDefault')
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Updating Existing Functions
|
|
162
|
+
|
|
163
|
+
You can re-deploy a function (for example, after you modified its code) by setting
|
|
164
|
+
the `override` parameter to `True`:
|
|
161
165
|
|
|
166
|
+
```python
|
|
167
|
+
client.deploy('add', add, 'Adds two numbers x and y', override=True)
|
|
162
168
|
```
|
|
163
169
|
|
|
170
|
+
Each re-deployment of an endpoint will increment its version number, which is also
|
|
171
|
+
returned as part of the query result.
|
|
172
|
+
|
|
173
|
+
If you do not want to modify the code of a function, you have the option to
|
|
174
|
+
update using `update_endpoint_info`. This allows users to update the
|
|
175
|
+
description, schema, or whether a function is public using just the function
|
|
176
|
+
name.
|
|
177
|
+
|
|
178
|
+
To update the description of an existing function:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
client.update_endpoint_info('add', description = 'Updated description for add')
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
To update whether an existing function is public:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
client.update_endpoint_info('add', is_public=True)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
To update the schema associated with an existing function:
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
updatedSchema = generate_schema(
|
|
194
|
+
input={'x': 3, 'y': 2},
|
|
195
|
+
output=5,
|
|
196
|
+
input_description={'x': 'first value',
|
|
197
|
+
'y': 'second value'},
|
|
198
|
+
output_description='the sum of x and y')
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
client.update_endpoint_info('add', schema=updatedSchema)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
To update `description`, `is_public`, and `schema` all at once:
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
client.update_endpoint_info('add',
|
|
208
|
+
is_public=True,
|
|
209
|
+
description='Updated description for add',
|
|
210
|
+
schema=updatedSchema)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Updating endpoints via `update_endpoint_info` will NOT increment version number
|
|
214
|
+
|
|
164
215
|
## Predeployed Functions
|
|
165
216
|
|
|
166
217
|
### Deploying Models Shipped With TabPy
|
|
@@ -39,9 +39,10 @@ def setup_package():
|
|
|
39
39
|
"Intended Audience :: Developers",
|
|
40
40
|
"Intended Audience :: Science/Research",
|
|
41
41
|
"License :: OSI Approved :: MIT License",
|
|
42
|
-
"Programming Language :: Python :: 3.7",
|
|
43
|
-
"Programming Language :: Python :: 3.8",
|
|
44
42
|
"Programming Language :: Python :: 3.9",
|
|
43
|
+
"Programming Language :: Python :: 3.10",
|
|
44
|
+
"Programming Language :: Python :: 3.11",
|
|
45
|
+
"Programming Language :: Python :: 3.12",
|
|
45
46
|
"Topic :: Scientific/Engineering",
|
|
46
47
|
"Topic :: Scientific/Engineering :: Information Analysis",
|
|
47
48
|
"Operating System :: Microsoft :: Windows",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.12.0
|
|
@@ -71,10 +71,12 @@ class EndpointHandler(ManagementHandler):
|
|
|
71
71
|
self.finish()
|
|
72
72
|
return
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
version_after_update = int(endpoints[name]["version"])
|
|
75
|
+
if request_data.get('should_update_version'):
|
|
76
|
+
version_after_update += 1
|
|
75
77
|
self.logger.log(logging.INFO, f"Endpoint info: {request_data}")
|
|
76
78
|
err_msg = yield self._add_or_update_endpoint(
|
|
77
|
-
"update", name,
|
|
79
|
+
"update", name, version_after_update, request_data
|
|
78
80
|
)
|
|
79
81
|
if err_msg:
|
|
80
82
|
self.error_out(400, err_msg)
|
|
@@ -84,6 +84,7 @@ class ManagementHandler(BaseHandler):
|
|
|
84
84
|
target = request_data.get("target", None)
|
|
85
85
|
schema = request_data.get("schema", None)
|
|
86
86
|
src_path = request_data.get("src_path", None)
|
|
87
|
+
is_public = request_data.get("is_public", None)
|
|
87
88
|
target_path = get_query_object_path(
|
|
88
89
|
self.settings[SettingsParameters.StateFilePath], name, version
|
|
89
90
|
)
|
|
@@ -117,6 +118,7 @@ class ManagementHandler(BaseHandler):
|
|
|
117
118
|
dependencies=dependencies,
|
|
118
119
|
target=target,
|
|
119
120
|
schema=schema,
|
|
121
|
+
is_public=is_public,
|
|
120
122
|
)
|
|
121
123
|
else:
|
|
122
124
|
self.tabpy_state.update_endpoint(
|
|
@@ -129,6 +131,7 @@ class ManagementHandler(BaseHandler):
|
|
|
129
131
|
target=target,
|
|
130
132
|
schema=schema,
|
|
131
133
|
version=version,
|
|
134
|
+
is_public=is_public,
|
|
132
135
|
)
|
|
133
136
|
|
|
134
137
|
except Exception as e:
|
|
@@ -187,6 +187,12 @@ class TabPyState:
|
|
|
187
187
|
|
|
188
188
|
return dependencies
|
|
189
189
|
|
|
190
|
+
def _check_and_set_is_public(self, is_public, defaultValue):
|
|
191
|
+
if is_public is None:
|
|
192
|
+
return defaultValue
|
|
193
|
+
|
|
194
|
+
return is_public
|
|
195
|
+
|
|
190
196
|
@state_lock
|
|
191
197
|
def add_endpoint(
|
|
192
198
|
self,
|
|
@@ -198,6 +204,7 @@ class TabPyState:
|
|
|
198
204
|
target=None,
|
|
199
205
|
dependencies=None,
|
|
200
206
|
schema=None,
|
|
207
|
+
is_public=None,
|
|
201
208
|
):
|
|
202
209
|
"""
|
|
203
210
|
Add a new endpoint to the TabPy.
|
|
@@ -231,6 +238,7 @@ class TabPyState:
|
|
|
231
238
|
docstring, "-- no docstring found in query function --")
|
|
232
239
|
endpoint_type = self._check_and_set_endpoint_type(endpoint_type, None)
|
|
233
240
|
dependencies = self._check_and_set_dependencies(dependencies, [])
|
|
241
|
+
is_public = self._check_and_set_is_public(is_public, False)
|
|
234
242
|
|
|
235
243
|
self._check_target(target)
|
|
236
244
|
if target and target not in endpoints:
|
|
@@ -246,6 +254,7 @@ class TabPyState:
|
|
|
246
254
|
"creation_time": int(time()),
|
|
247
255
|
"last_modified_time": int(time()),
|
|
248
256
|
"schema": schema,
|
|
257
|
+
"is_public": is_public,
|
|
249
258
|
}
|
|
250
259
|
|
|
251
260
|
endpoints[name] = endpoint_info
|
|
@@ -289,6 +298,7 @@ class TabPyState:
|
|
|
289
298
|
target=None,
|
|
290
299
|
dependencies=None,
|
|
291
300
|
schema=None,
|
|
301
|
+
is_public=None,
|
|
292
302
|
):
|
|
293
303
|
"""
|
|
294
304
|
Update an existing endpoint on the TabPy.
|
|
@@ -330,6 +340,7 @@ class TabPyState:
|
|
|
330
340
|
endpoint_type, endpoint_info["type"])
|
|
331
341
|
dependencies = self._check_and_set_dependencies(
|
|
332
342
|
dependencies, endpoint_info.get("dependencies", []))
|
|
343
|
+
is_public = self._check_and_set_is_public(is_public, endpoint_info["is_public"])
|
|
333
344
|
|
|
334
345
|
self._check_target(target)
|
|
335
346
|
if target and target not in endpoints:
|
|
@@ -352,6 +363,7 @@ class TabPyState:
|
|
|
352
363
|
"creation_time": endpoint_info["creation_time"],
|
|
353
364
|
"last_modified_time": int(time()),
|
|
354
365
|
"schema": schema,
|
|
366
|
+
"is_public": is_public,
|
|
355
367
|
}
|
|
356
368
|
|
|
357
369
|
endpoints[name] = endpoint_info
|
|
@@ -173,7 +173,8 @@ class Client:
|
|
|
173
173
|
"dependencies": [],
|
|
174
174
|
"last_modified_time": 1469511182,
|
|
175
175
|
"type": "model",
|
|
176
|
-
"target": null
|
|
176
|
+
"target": null,
|
|
177
|
+
"is_public": True}
|
|
177
178
|
"add": {
|
|
178
179
|
"description": "",
|
|
179
180
|
"docstring": "-- no docstring found in query function --",
|
|
@@ -182,7 +183,8 @@ class Client:
|
|
|
182
183
|
"dependencies": [],
|
|
183
184
|
"last_modified_time": 1469505967,
|
|
184
185
|
"type": "model",
|
|
185
|
-
"target": null
|
|
186
|
+
"target": null,
|
|
187
|
+
"is_public": False}
|
|
186
188
|
}
|
|
187
189
|
"""
|
|
188
190
|
return self._service.get_endpoints(type)
|
|
@@ -191,7 +193,7 @@ class Client:
|
|
|
191
193
|
"""Returns the endpoint upload destination."""
|
|
192
194
|
return self._service.get_endpoint_upload_destination()["path"]
|
|
193
195
|
|
|
194
|
-
def deploy(self, name, obj, description="", schema=None, override=False):
|
|
196
|
+
def deploy(self, name, obj, description="", schema=None, override=False, is_public=False):
|
|
195
197
|
"""Deploys a Python function as an endpoint in the server.
|
|
196
198
|
|
|
197
199
|
Parameters
|
|
@@ -220,6 +222,12 @@ class Client:
|
|
|
220
222
|
RuntimeError. If True and there is already an endpoint with that
|
|
221
223
|
name, it will deploy a new version on top of it.
|
|
222
224
|
|
|
225
|
+
is_public : bool, optional
|
|
226
|
+
Whether a function should be public for viewing from within tableau. If
|
|
227
|
+
False, function will not appear in the custom functions explorer within
|
|
228
|
+
Tableau. If True, function will be visible ta anyone on a site with this
|
|
229
|
+
analytics extension configured
|
|
230
|
+
|
|
223
231
|
See Also
|
|
224
232
|
--------
|
|
225
233
|
remove, get_endpoints
|
|
@@ -236,14 +244,14 @@ class Client:
|
|
|
236
244
|
|
|
237
245
|
version = endpoint.version + 1
|
|
238
246
|
|
|
239
|
-
obj = self._gen_endpoint(name, obj, description, version, schema)
|
|
247
|
+
obj = self._gen_endpoint(name, obj, description, version, schema, is_public)
|
|
240
248
|
|
|
241
249
|
self._upload_endpoint(obj)
|
|
242
250
|
|
|
243
251
|
if version == 1:
|
|
244
252
|
self._service.add_endpoint(Endpoint(**obj))
|
|
245
253
|
else:
|
|
246
|
-
self._service.set_endpoint(Endpoint(**obj))
|
|
254
|
+
self._service.set_endpoint(Endpoint(**obj), should_update_version=True)
|
|
247
255
|
|
|
248
256
|
self._wait_for_endpoint_deployment(obj["name"], obj["version"])
|
|
249
257
|
|
|
@@ -256,7 +264,68 @@ class Client:
|
|
|
256
264
|
Endpoint name to remove'''
|
|
257
265
|
self._service.remove_endpoint(name)
|
|
258
266
|
|
|
259
|
-
def
|
|
267
|
+
def update_endpoint_info(self, name, description=None, schema=None, is_public=None):
|
|
268
|
+
'''Updates description, schema, or is public for an existing endpoint
|
|
269
|
+
|
|
270
|
+
Parameters
|
|
271
|
+
----------
|
|
272
|
+
name : str
|
|
273
|
+
Name of the endpoint that to be updated. If endpoint does not exist
|
|
274
|
+
runtime error will be thrown
|
|
275
|
+
|
|
276
|
+
description : str, optional
|
|
277
|
+
The description for the endpoint. This string will be returned by
|
|
278
|
+
the ``endpoints`` API.
|
|
279
|
+
|
|
280
|
+
schema : dict, optional
|
|
281
|
+
The schema of the function, containing information about input and
|
|
282
|
+
output parameters, and respective examples. Providing a schema for
|
|
283
|
+
a deployed function lets other users of the service discover how to
|
|
284
|
+
use it. Refer to schema.generate_schema for more information on
|
|
285
|
+
how to generate the schema.
|
|
286
|
+
|
|
287
|
+
is_public : bool, optional
|
|
288
|
+
Whether a function should be public for viewing from within tableau. If
|
|
289
|
+
False, function will not appear in the custom functions explorer within
|
|
290
|
+
Tableau. If True, function will be visible to anyone on a site with this
|
|
291
|
+
analytics extension configured
|
|
292
|
+
'''
|
|
293
|
+
|
|
294
|
+
endpoint = self.get_endpoints().get(name)
|
|
295
|
+
|
|
296
|
+
if not endpoint:
|
|
297
|
+
raise RuntimeError(
|
|
298
|
+
f"No endpoint with that name ({name}) exists"
|
|
299
|
+
" Please select an existing endpoint to update"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
if description is not None:
|
|
303
|
+
if type(description) is not str:
|
|
304
|
+
raise RuntimeError(
|
|
305
|
+
f"Type of description must be string"
|
|
306
|
+
)
|
|
307
|
+
endpoint.description = description
|
|
308
|
+
if schema is not None:
|
|
309
|
+
if type(schema) is not dict:
|
|
310
|
+
raise RuntimeError(
|
|
311
|
+
f"Type of schema must be dictionary"
|
|
312
|
+
)
|
|
313
|
+
endpoint.schema = schema
|
|
314
|
+
if is_public is not None:
|
|
315
|
+
if type(is_public) is not bool:
|
|
316
|
+
raise RuntimeError(
|
|
317
|
+
f"Type of is_public must be bool"
|
|
318
|
+
)
|
|
319
|
+
endpoint.is_public = is_public
|
|
320
|
+
|
|
321
|
+
dest_path = self._get_endpoint_upload_destination()
|
|
322
|
+
|
|
323
|
+
endpoint.src_path = os.path.join(
|
|
324
|
+
dest_path, "endpoints", endpoint.name, str(endpoint.version)
|
|
325
|
+
)
|
|
326
|
+
self._service.set_endpoint(endpoint, should_update_version=False)
|
|
327
|
+
|
|
328
|
+
def _gen_endpoint(self, name, obj, description, version=1, schema=None, is_public=False):
|
|
260
329
|
"""Generates an endpoint dict.
|
|
261
330
|
|
|
262
331
|
Parameters
|
|
@@ -274,6 +343,10 @@ class Client:
|
|
|
274
343
|
version : int
|
|
275
344
|
The version. Defaults to 1.
|
|
276
345
|
|
|
346
|
+
is_public : bool
|
|
347
|
+
True if function should be visible in the custom functions explorer
|
|
348
|
+
within Tableau
|
|
349
|
+
|
|
277
350
|
Returns
|
|
278
351
|
-------
|
|
279
352
|
dict
|
|
@@ -318,6 +391,7 @@ class Client:
|
|
|
318
391
|
"required_files": [],
|
|
319
392
|
"required_packages": [],
|
|
320
393
|
"schema": copy.copy(schema),
|
|
394
|
+
"is_public": is_public,
|
|
321
395
|
}
|
|
322
396
|
|
|
323
397
|
def _upload_endpoint(self, obj):
|
|
@@ -47,6 +47,7 @@ class Endpoint(RESTObject):
|
|
|
47
47
|
evaluator = RESTProperty(str)
|
|
48
48
|
schema_version = RESTProperty(int)
|
|
49
49
|
schema = RESTProperty(str)
|
|
50
|
+
is_public = RESTProperty(bool)
|
|
50
51
|
|
|
51
52
|
def __new__(cls, **kwargs):
|
|
52
53
|
"""Dispatch to the appropriate class."""
|
|
@@ -67,6 +68,7 @@ class Endpoint(RESTObject):
|
|
|
67
68
|
and self.evaluator == other.evaluator
|
|
68
69
|
and self.schema_version == other.schema_version
|
|
69
70
|
and self.schema == other.schema
|
|
71
|
+
and self.is_public == other.is_public
|
|
70
72
|
)
|
|
71
73
|
|
|
72
74
|
|
|
@@ -202,7 +204,7 @@ class RESTServiceClient:
|
|
|
202
204
|
"""
|
|
203
205
|
return self.service_client.POST("endpoints", endpoint.to_json())
|
|
204
206
|
|
|
205
|
-
def set_endpoint(self, endpoint):
|
|
207
|
+
def set_endpoint(self, endpoint, should_update_version=True):
|
|
206
208
|
"""Updates an endpoint through the management API.
|
|
207
209
|
|
|
208
210
|
Parameters
|
|
@@ -211,8 +213,16 @@ class RESTServiceClient:
|
|
|
211
213
|
endpoint : Endpoint
|
|
212
214
|
|
|
213
215
|
The endpoint to update.
|
|
216
|
+
|
|
217
|
+
should_update_version : bool
|
|
218
|
+
|
|
219
|
+
Whether this update should increment the version.
|
|
220
|
+
False if this is called from update_endpoint_info
|
|
221
|
+
True if called from deploy
|
|
214
222
|
"""
|
|
215
|
-
|
|
223
|
+
request_body = endpoint.to_json()
|
|
224
|
+
request_body["should_update_version"] = should_update_version
|
|
225
|
+
return self.service_client.PUT("endpoints/" + endpoint.name, request_body)
|
|
216
226
|
|
|
217
227
|
def remove_endpoint(self, endpoint_name):
|
|
218
228
|
"""Deletes an endpoint through the management API.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tabpy
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.12.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
|
|
@@ -21,9 +21,10 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
21
21
|
Classifier: Intended Audience :: Developers
|
|
22
22
|
Classifier: Intended Audience :: Science/Research
|
|
23
23
|
Classifier: License :: OSI Approved :: MIT License
|
|
24
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
25
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
26
24
|
Classifier: Programming Language :: Python :: 3.9
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
27
28
|
Classifier: Topic :: Scientific/Engineering
|
|
28
29
|
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
29
30
|
Classifier: Operating System :: Microsoft :: Windows
|
|
@@ -66,6 +67,22 @@ scripts and saved functions via Tableau's table calculations.
|
|
|
66
67
|
|
|
67
68
|
# Changelog
|
|
68
69
|
|
|
70
|
+
## v2.12.0
|
|
71
|
+
|
|
72
|
+
### Improvements
|
|
73
|
+
|
|
74
|
+
- Add support for public deployed functions that will be visible to users
|
|
75
|
+
in Tableau when using the Custom Functions Explorer
|
|
76
|
+
- Add functionality to allow users to update existing deployed functions
|
|
77
|
+
without needing to redeploy the function itself
|
|
78
|
+
|
|
79
|
+
## v2.11.0
|
|
80
|
+
|
|
81
|
+
### Improvements
|
|
82
|
+
|
|
83
|
+
- Add support for Python 3.10, 3.11, and 3.12. End support Python for
|
|
84
|
+
3.7 and 3.8.
|
|
85
|
+
|
|
69
86
|
## v2.10.0
|
|
70
87
|
|
|
71
88
|
### Improvements
|
tabpy-2.10.0/tabpy/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.10.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
|
{tabpy-2.10.0 → tabpy-2.12.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
|
|
File without changes
|
{tabpy-2.10.0 → tabpy-2.12.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
|
|
File without changes
|