Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-tabpy for openSUSE:Factory checked in at 2022-04-20 16:56:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-tabpy (Old) and /work/SRC/openSUSE:Factory/.python-tabpy.new.1941 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-tabpy" Wed Apr 20 16:56:24 2022 rev:4 rq:970823 version:2.5.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-tabpy/python-tabpy.changes 2021-02-15 23:20:36.995782609 +0100 +++ /work/SRC/openSUSE:Factory/.python-tabpy.new.1941/python-tabpy.changes 2022-04-20 16:56:49.070594343 +0200 @@ -1,0 +2,24 @@ +Tue Apr 19 12:18:05 UTC 2022 - pgaj...@suse.com + +- version update to 2.5.0 + ## v2.5.0 + ### Improvements + - A server with Adhoc Disabled Flag on with the wrong credentials will now + return wrong credentials error instead of telling the user + that Adhoc Scripts are not allowed on this server. + - Added documentation for how to run TabPy projects with local changes + ### Breaking changes + - Discontinued support for Python 3.6 + - Added support for Python 3.9 + ## v2.4.0 + ### Improvements + - Add toggle to turn off evaluate API. + ### Breaking changes + - Changing error code to 406 when server not configured for authentication + but credentials are provided by client. + ## v2.3.2 + ### Improvements + - Test files added to tar.gz and zip releases. +- python-mock is not required for build + +------------------------------------------------------------------- Old: ---- tabpy-2.3.1.tar.gz New: ---- tabpy-2.5.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-tabpy.spec ++++++ --- /var/tmp/diff_new_pack.BF4Oi9/_old 2022-04-20 16:56:49.634594870 +0200 +++ /var/tmp/diff_new_pack.BF4Oi9/_new 2022-04-20 16:56:49.638594874 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-tabpy # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -20,7 +20,7 @@ %define skip_python2 1 %define skip_python36 1 Name: python-tabpy -Version: 2.3.1 +Version: 2.5.0 Release: 0 Summary: Tableau Python service License: MIT @@ -41,8 +41,8 @@ Recommends: python-nltk Recommends: python-numpy Recommends: python-pandas -Recommends: python-scipy Recommends: python-scikit-learn +Recommends: python-scipy Suggests: python-textblob BuildArch: noarch # SECTION test requirements @@ -52,7 +52,6 @@ BuildRequires: %{python_module docopt} BuildRequires: %{python_module genson} BuildRequires: %{python_module jsonschema} -BuildRequires: %{python_module mock} BuildRequires: %{python_module nltk} BuildRequires: %{python_module numpy} BuildRequires: %{python_module pandas} @@ -92,7 +91,7 @@ } %check -mkdir ~/bin +mkdir -p ~/bin export PATH=~/bin:$PATH %{python_expand export PYTHONPATH=%{buildroot}%{$python_sitelib} cp %{buildroot}/%{_bindir}/tabpy-%{$python_bin_suffix} ~/bin/tabpy ++++++ tabpy-2.3.1.tar.gz -> tabpy-2.5.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/.github/workflows/coverage.yml new/TabPy-2.5.0/.github/workflows/coverage.yml --- old/TabPy-2.3.1/.github/workflows/coverage.yml 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/.github/workflows/coverage.yml 2022-01-20 22:20:43.000000000 +0100 @@ -27,8 +27,13 @@ pip install -r requirements_dev.txt - name: Test with pytest - run: | + run: pytest tests --cov=tabpy --cov-config=setup.cfg - coveralls env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + + - name: Run coveralls + run: + coveralls --service=github + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/.github/workflows/docker-publish.yml new/TabPy-2.5.0/.github/workflows/docker-publish.yml --- old/TabPy-2.3.1/.github/workflows/docker-publish.yml 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/.github/workflows/docker-publish.yml 2022-01-20 22:20:43.000000000 +0100 @@ -2,7 +2,7 @@ on: release: - types: [released] + types: [published] env: IMAGE_NAME: tabpy diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/.github/workflows/pull_request.yml new/TabPy-2.5.0/.github/workflows/pull_request.yml --- old/TabPy-2.3.1/.github/workflows/pull_request.yml 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/.github/workflows/pull_request.yml 2022-01-20 22:20:43.000000000 +0100 @@ -9,7 +9,7 @@ strategy: matrix: - python-version: [3.6, 3.7, 3.8] + python-version: [3.7, 3.8, 3.9] os: [ubuntu-latest, windows-latest, macos-latest] steps: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/.github/workflows/push.yml new/TabPy-2.5.0/.github/workflows/push.yml --- old/TabPy-2.3.1/.github/workflows/push.yml 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/.github/workflows/push.yml 2022-01-20 22:20:43.000000000 +0100 @@ -9,7 +9,7 @@ strategy: matrix: - python-version: [3.6, 3.7, 3.8] + python-version: [3.7, 3.8, 3.9] os: [ubuntu-latest, windows-latest, macos-latest] steps: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/CHANGELOG new/TabPy-2.5.0/CHANGELOG --- old/TabPy-2.3.1/CHANGELOG 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/CHANGELOG 2022-01-20 22:20:43.000000000 +0100 @@ -1,5 +1,36 @@ # Changelog +## v2.5.0 + +### Improvements + +- A server with Adhoc Disabled Flag on with the wrong credentials will now + return wrong credentials error instead of telling the user + that Adhoc Scripts are not allowed on this server. +- Added documentation for how to run TabPy projects with local changes + +### Breaking changes + +- Discontinued support for Python 3.6 +- Added support for Python 3.9 + +## v2.4.0 + +### Improvements + +- Add toggle to turn off evaluate API. + +### Breaking changes + +- Changing error code to 406 when server not configured for authentication + but credentials are provided by client. + +## v2.3.2 + +### Improvements + +- Test files added to tar.gz and zip releases. + ## v2.3.1 ### Bug fixes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/CONTRIBUTING.md new/TabPy-2.5.0/CONTRIBUTING.md --- old/TabPy-2.3.1/CONTRIBUTING.md 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/CONTRIBUTING.md 2022-01-20 22:20:43.000000000 +0100 @@ -30,7 +30,7 @@ These are prerequisites for an environment required for a contributor to be able to work on TabPy changes: -- Python 3.6, 3.7 or 3.8: +- Python 3.7, 3.8 or 3.9: - To see which version of Python you have installed, run `python --version`. - git - Node.js for npm packages - install from <https://nodejs.org>. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/Dockerfile new/TabPy-2.5.0/Dockerfile --- old/TabPy-2.3.1/Dockerfile 1970-01-01 01:00:00.000000000 +0100 +++ new/TabPy-2.5.0/Dockerfile 2022-01-20 22:20:43.000000000 +0100 @@ -0,0 +1,14 @@ +FROM python:3 + +WORKDIR /app + +# install the latest TabPy +RUN python3 -m pip install --upgrade pip && python3 -m pip install --upgrade tabpy + +# start TabPy +CMD ["sh", "-c", "tabpy"] + +# run startup script +ADD start.sh / +RUN chmod +x /start.sh +CMD ["/start.sh"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/MANIFEST.in new/TabPy-2.5.0/MANIFEST.in --- old/TabPy-2.3.1/MANIFEST.in 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/MANIFEST.in 2022-01-20 22:20:43.000000000 +0100 @@ -3,8 +3,21 @@ include \ CHANGELOG \ - LICENSE \ + LICENSE \ + README.md \ + Procfile \ tabpy/VERSION \ tabpy/tabpy_server/state.ini.template \ tabpy/tabpy_server/static/* \ tabpy/tabpy_server/common/default.conf + +# Docs and tests +include requirements_dev.txt requirements.txt +recursive-include docs *.md +recursive-include docs *.png +recursive-include misc *.json +recursive-include misc *.yml +recursive-include tests *.conf +recursive-include tests *.crt +recursive-include tests *.key +recursive-include tests *.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/Procfile new/TabPy-2.5.0/Procfile --- old/TabPy-2.3.1/Procfile 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/Procfile 2022-01-20 22:20:43.000000000 +0100 @@ -1 +1 @@ -web: export TABPY_PORT=$PORT && tabpy \ No newline at end of file +web: export TABPY_PORT=$PORT && export TABPY_PWD_FILE=./file.txt && tabpy-user add -u $USERNAME -p $PASSWORD -f ./file.txt && tabpy \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/README.md new/TabPy-2.5.0/README.md --- old/TabPy-2.3.1/README.md 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/README.md 2022-01-20 22:20:43.000000000 +0100 @@ -11,7 +11,7 @@ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/tabpy?label=PyPI%20Python%20versions) [![PyPI version](https://badge.fury.io/py/tabpy.svg)](https://pypi.python.org/pypi/tabpy/) -[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) +[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/tableau/tabpy) TabPy (the Tableau Python Server) is an Analytics Extension implementation which expands Tableau's capabilities by allowing users to execute Python scripts and @@ -52,5 +52,3 @@ * [TabPy Tutorial on TabWiki](https://community.tableau.com/docs/DOC-10856) ![GitHub commit activity](https://img.shields.io/github/commit-activity/m/tableau/TabPy.svg) -![GitHub forks](https://img.shields.io/github/forks/tableau/TabPy.svg?label=Forks&style=social) -![GitHub stars](https://img.shields.io/github/stars/tableau/TabPy.svg?style=social) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/app.json new/TabPy-2.5.0/app.json --- old/TabPy-2.3.1/app.json 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/app.json 2022-01-20 22:20:43.000000000 +0100 @@ -2,6 +2,18 @@ "name": "TabPy", "description": "Analytics Extension implementation which expands Tableau's capabilities by allowing users to execute Python scripts and saved functions via Tableau's table calculations.", "repository": "https://github.com/tableau/TabPy", - "logo": "", - "keywords": ["tableau", "python", "analytics-extension"] + "logo": "https://raw.githubusercontent.com/tableau/TabPy/master/tabpy/tabpy_server/static/TabPy_logo.png", + "keywords": ["tableau", "python", "analytics-extension"], + "env":{ + "USERNAME":{ + "description": "Add your username", + "value": "gzanolli", + "required": true + }, + "PASSWORD":{ + "description": "Define your password", + "value": "P@ssw0rd", + "required": true + } + } } \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/codeql-analysis.yml new/TabPy-2.5.0/codeql-analysis.yml --- old/TabPy-2.3.1/codeql-analysis.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/TabPy-2.5.0/codeql-analysis.yml 2022-01-20 22:20:43.000000000 +0100 @@ -0,0 +1,71 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master, dev ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '0 22 * * 3' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ?????? Command-line programs to run using the OS shell. + # ???? https://git.io/JvXDl + + # ?????? If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/docs/TableauConfiguration.md new/TabPy-2.5.0/docs/TableauConfiguration.md --- old/TabPy-2.3.1/docs/TableauConfiguration.md 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/docs/TableauConfiguration.md 2022-01-20 22:20:43.000000000 +0100 @@ -1,16 +1,20 @@ # Using Python in Tableau Calculations <!-- markdownlint-disable MD004 --> + <!-- toc --> - [Configuration](#configuration) * [Tableau Desktop](#tableau-desktop) - * [Tableau Server 2018.2 and Newer Versions](#tableau-server-20182-and-newer-versions) + * [Tableau Server 2020.2 and Newer Versions](#tableau-server-20202-and-newer-versions) + * [Tableau Server 2020.1 and older](#tableau-server-20201-and-older) + * [Tableau Server 2018.2 and 2018.3](#tableau-server-20182-and-20183) * [Tableau Server 2018.1 and Older Versions](#tableau-server-20181-and-older-versions) - [Anatomy of a Python Calculation](#anatomy-of-a-python-calculation) - [Using Deployed Functions](#using-deployed-functions) <!-- tocstop --> + <!-- markdownlint-enable MD004 --> ## Configuration @@ -25,7 +29,24 @@ [Configure an Analytics Extension connection](https://help.tableau.com/current/pro/desktop/en-us/r_connection_manage.htm#configure-an-external-service-connection) documentation page. -### Tableau Server 2018.2 and Newer Versions +### Tableau Server 2020.2 and Newer Versions + +Starting from Tableau Server 2020.2 analytics extensions connections +are configured on site level as shown of +[Configure Connections to Analytics Extensions](https://help.tableau.com/current/server/en-us/config_r_tabpy.htm) +page. + +### Tableau Server 2020.1 and older + +For older Tableau Server versions refer to version specific documentation: + +- [2020.1](https://help.tableau.com/v2020.1/server/en-us/config_r_tabpy.htm). +- [2019.4](https://help.tableau.com/v2019.4/server/en-us/config_r_tabpy.htm). +- [2019.3](https://help.tableau.com/v2019.3/server/en-us/cli_security_tsm.htm#tsm_security_vizql-extsvc-ssl-enable). +- [2019.2](https://help.tableau.com/v2019.2/server/en-us/cli_security_tsm.htm#tsm_security_vizql-extsvc-ssl-enable). +- [2019.1](https://help.tableau.com/v2019.1/server/en-us/cli_security_tsm.htm#tsm_security_vizql-extsvc-ssl-enable). + +### Tableau Server 2018.2 and 2018.3 To configure Tableau Server 2018.2 and newer versions to connect to TabPy server follow instructions on Tableau diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/docs/security.md new/TabPy-2.5.0/docs/security.md --- old/TabPy-2.3.1/docs/security.md 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/docs/security.md 2022-01-20 22:20:43.000000000 +0100 @@ -14,3 +14,6 @@ - Install new Python packages which can contain binary code. - Execute operating system commands. - Open network connections to other servers and download files. +- Execution of ad-hoc Python scripts can be disabled by turning off the + /evaluate endpoint. To disable /evaluate endpoint, set "TABPY_EVALUATE_ENABLE" + to false in config file. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/docs/server-config.md new/TabPy-2.5.0/docs/server-config.md --- old/TabPy-2.3.1/docs/server-config.md 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/docs/server-config.md 2022-01-20 22:20:43.000000000 +0100 @@ -87,6 +87,8 @@ - `TABPY_MAX_REQUEST_SIZE_MB` - maximal request size supported by TabPy server in Megabytes. All requests of exceeding size are rejected. Default value is 100 Mb. +- `TABPY_EVALUATE_ENABLE` - enable evaluate api to execute ad-hoc Python scripts + Default value - `true`. - `TABPY_EVALUATE_TIMEOUT` - script evaluation timeout in seconds. Default value - `30`. This timeout does not apply when evaluating models either through the `/query` method, or using the `tabpy.query(...)` syntax with @@ -125,6 +127,10 @@ # Default value is 100 Mb. # TABPY_MAX_REQUEST_SIZE_MB = 100 +# Enable evaluate api to execute ad-hoc Python scripts +# Enabled by default. Disabling it will result in 404 error. +# TABPY_EVALUATE_ENABLE = true + # Configure how long a custom script provided to the /evaluate method # will run before throwing a TimeoutError. # The value should be a float representing the timeout time in seconds. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/docs/server-install.md new/TabPy-2.5.0/docs/server-install.md --- old/TabPy-2.3.1/docs/server-install.md 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/docs/server-install.md 2022-01-20 22:20:43.000000000 +0100 @@ -47,3 +47,19 @@ It is highly recommended to use Python virtual environment for running TabPy. Check the [Running TabPy in Python Virtual Environment](tabpy-virtualenv.md) page for more details. + +## Starting a Local TabPy Project + +To create a version of TabPy that incorporates locally-made changes, +use pip to create a package from your local TabPy project +and install it within that directory (preferably a virtual environment): + +```sh +pip install -e . +``` + +Then start TabPy just like it was mentioned earlier + +```sh +tabpy +``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/docs/tabpy-tools.md new/TabPy-2.5.0/docs/tabpy-tools.md --- old/TabPy-2.3.1/docs/tabpy-tools.md 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/docs/tabpy-tools.md 2022-01-20 22:20:43.000000000 +0100 @@ -18,6 +18,7 @@ - [Providing Schema Metadata](#providing-schema-metadata) - [Querying an Endpoint](#querying-an-endpoint) - [Evaluating Arbitrary Python Scripts](#evaluating-arbitrary-python-scripts) +- [Deploying Models in TabPy Docker Container](#deploying-models-in-tabpy-docker-container) <!-- tocstop --> @@ -38,7 +39,7 @@ The URL and port are where the Tableau-Python-Server process has been started - more info can be found in the -[server section](server-startup.md#Command-Line-Arguments) of the documentation. +[Starting TabPy](server-install.md#starting-tabpy) section of the documentation. ## Authentication @@ -433,3 +434,16 @@ The convention for this is to use a provided function call `tabpy.query` in the code, which behaves like the `query` method in `tabpy-tools`. See the [REST API documentation](server-rest.md) for an example. + +## Deploying Models in TabPy Docker Container + +To deploy custom models for TabPy running in docker container, first copy all +python model files onto host machine. + +For example, `myFunction.py` is the model we want to deploy. +Run following from the folder containing `myFunction.py` on host machine + +```console +docker cp myFunction.py <container_id>:/app/scripts/myFunction.py +docker exec -it <container_id> python /app/scripts/myFunction.py +``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/docs/tabpy-virtualenv.md new/TabPy-2.5.0/docs/tabpy-virtualenv.md --- old/TabPy-2.3.1/docs/tabpy-virtualenv.md 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/docs/tabpy-virtualenv.md 2022-01-20 22:20:43.000000000 +0100 @@ -33,10 +33,27 @@ ``` 4. Run TabPy: + 1. Default TabPy - ```sh - tabpy - ``` + ```sh + tabpy + ``` + + 2. Local TabPy + + To create a version of TabPy that incorporates locally-made changes, + use pip to create a package from your local TabPy project and install + it within that directory: + + ```sh + pip install -e . + ``` + + Then start TabPy just like it was mentioned earlier + + ```sh + tabpy + ``` 5. To deactivate virtual environment run: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/setup.py new/TabPy-2.5.0/setup.py --- old/TabPy-2.3.1/setup.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/setup.py 2022-01-20 22:20:43.000000000 +0100 @@ -39,9 +39,9 @@ "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Information Analysis", "Operating System :: Microsoft :: Windows", @@ -60,7 +60,7 @@ "tabpy_server/common/default.conf", ] }, - python_requires=">=3.6", + python_requires=">=3.7", license="MIT", # Note: many of these required packages are included in base python # but are listed here because different linux distros use custom diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/start.sh new/TabPy-2.5.0/start.sh --- old/TabPy-2.3.1/start.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/TabPy-2.5.0/start.sh 2022-01-20 22:20:43.000000000 +0100 @@ -0,0 +1,23 @@ +#!/bin/bash + +# Start tabpy +tabpy & + +# Wait for server to become available, wait 1 min maximum +attempt_counter=0 +max_attempts=20 + +until $(curl --output /dev/null --silent --head --fail localhost:9004); do + if [ ${attempt_counter} -eq ${max_attempts} ];then + echo "Maximum attempts reached, tabpy server not started" + exit 1 + fi + + echo "Waiting for tabpy server" + attempt_counter=$(($attempt_counter+1)) + sleep 3 +done + +# Deploy tabpy models +tabpy-deploy-models & +wait \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tabpy/VERSION new/TabPy-2.5.0/tabpy/VERSION --- old/TabPy-2.3.1/tabpy/VERSION 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tabpy/VERSION 2022-01-20 22:20:43.000000000 +0100 @@ -1 +1 @@ -2.3.1 \ No newline at end of file +2.5.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tabpy/tabpy_server/app/app.py new/TabPy-2.5.0/tabpy/tabpy_server/app/app.py --- old/TabPy-2.3.1/tabpy/tabpy_server/app/app.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tabpy/tabpy_server/app/app.py 2022-01-20 22:20:43.000000000 +0100 @@ -18,6 +18,7 @@ EndpointHandler, EndpointsHandler, EvaluationPlaneHandler, + EvaluationPlaneDisabledHandler, QueryPlaneHandler, ServiceInfoHandler, StatusHandler, @@ -150,7 +151,8 @@ ), ( self.subdirectory + r"/evaluate", - EvaluationPlaneHandler, + EvaluationPlaneHandler if self.settings[SettingsParameters.EvaluateEnabled] + else EvaluationPlaneDisabledHandler, dict(executor=executor, app=self), ), ( @@ -259,6 +261,8 @@ settings_parameters = [ (SettingsParameters.Port, ConfigParameters.TABPY_PORT, 9004, None), (SettingsParameters.ServerVersion, None, __version__, None), + (SettingsParameters.EvaluateEnabled, ConfigParameters.TABPY_EVALUATE_ENABLE, + True, parser.getboolean), (SettingsParameters.EvaluateTimeout, ConfigParameters.TABPY_EVALUATE_TIMEOUT, 30, parser.getfloat), (SettingsParameters.UploadDir, ConfigParameters.TABPY_QUERY_OBJECT_PATH, @@ -412,6 +416,7 @@ "methods": {"basic-auth": {}}, } + features["evaluate_enabled"] = self.settings[SettingsParameters.EvaluateEnabled] return features def _build_tabpy_state(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tabpy/tabpy_server/app/app_parameters.py new/TabPy-2.5.0/tabpy/tabpy_server/app/app_parameters.py --- old/TabPy-2.3.1/tabpy/tabpy_server/app/app_parameters.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tabpy/tabpy_server/app/app_parameters.py 2022-01-20 22:20:43.000000000 +0100 @@ -14,6 +14,7 @@ TABPY_LOG_DETAILS = "TABPY_LOG_DETAILS" TABPY_STATIC_PATH = "TABPY_STATIC_PATH" TABPY_MAX_REQUEST_SIZE_MB = "TABPY_MAX_REQUEST_SIZE_MB" + TABPY_EVALUATE_ENABLE = "TABPY_EVALUATE_ENABLE" TABPY_EVALUATE_TIMEOUT = "TABPY_EVALUATE_TIMEOUT" @@ -34,3 +35,4 @@ StaticPath = "static_path" MaxRequestSizeInMb = "max_request_size_in_mb" EvaluateTimeout = "evaluate_timeout" + EvaluateEnabled = "evaluate_enabled" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tabpy/tabpy_server/common/default.conf new/TabPy-2.5.0/tabpy/tabpy_server/common/default.conf --- old/TabPy-2.3.1/tabpy/tabpy_server/common/default.conf 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tabpy/tabpy_server/common/default.conf 2022-01-20 22:20:43.000000000 +0100 @@ -25,6 +25,10 @@ # Default value is 100 Mb. # TABPY_MAX_REQUEST_SIZE_MB = 100 +# Enable evaluate api to execute ad-hoc Python scripts +# Enabled by default. Disabling it will result in 404 error. +# TABPY_EVALUATE_ENABLE = true + # Configure how long a custom script provided to the /evaluate method # will run before throwing a TimeoutError. # The value should be a float representing the timeout time in seconds. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tabpy/tabpy_server/handlers/__init__.py new/TabPy-2.5.0/tabpy/tabpy_server/handlers/__init__.py --- old/TabPy-2.3.1/tabpy/tabpy_server/handlers/__init__.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tabpy/tabpy_server/handlers/__init__.py 2022-01-20 22:20:43.000000000 +0100 @@ -3,6 +3,7 @@ from tabpy.tabpy_server.handlers.endpoint_handler import EndpointHandler from tabpy.tabpy_server.handlers.endpoints_handler import EndpointsHandler +from tabpy.tabpy_server.handlers.evaluation_plane_handler import EvaluationPlaneDisabledHandler from tabpy.tabpy_server.handlers.evaluation_plane_handler import EvaluationPlaneHandler from tabpy.tabpy_server.handlers.query_plane_handler import QueryPlaneHandler from tabpy.tabpy_server.handlers.service_info_handler import ServiceInfoHandler diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tabpy/tabpy_server/handlers/base_handler.py new/TabPy-2.5.0/tabpy/tabpy_server/handlers/base_handler.py --- old/TabPy-2.3.1/tabpy/tabpy_server/handlers/base_handler.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tabpy/tabpy_server/handlers/base_handler.py 2022-01-20 22:20:43.000000000 +0100 @@ -421,7 +421,7 @@ def fail_with_auth_error(self): """ - Prepares server 401 response and server 400 response depending + Prepares server 401 response and server 406 response depending on the value of the self.auth_error flag """ if self.auth_error == AuthErrorStates.NotAuthorized: @@ -434,11 +434,11 @@ log_message="Invalid credentials provided.", ) else: - self.logger.log(logging.ERROR, "Failing with 400 for Bad Request") - self.set_status(400) + self.logger.log(logging.ERROR, "Failing with 406 for Not Acceptable") + self.set_status(406) self.set_header("WWW-Authenticate", f'Basic realm="{self.tabpy_state.name}"') self.error_out( - 400, - info="Bad request.", - log_message="Username or Password provided when authentication not available", + 406, + info="Not Acceptable", + log_message="Username or password provided when authentication not available.", ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tabpy/tabpy_server/handlers/evaluation_plane_handler.py new/TabPy-2.5.0/tabpy/tabpy_server/handlers/evaluation_plane_handler.py --- old/TabPy-2.3.1/tabpy/tabpy_server/handlers/evaluation_plane_handler.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tabpy/tabpy_server/handlers/evaluation_plane_handler.py 2022-01-20 22:20:43.000000000 +0100 @@ -29,6 +29,24 @@ return response.json() +class EvaluationPlaneDisabledHandler(BaseHandler): + """ + EvaluationPlaneDisabledHandler responds with error message when ad-hoc scripts have been disabled. + """ + + def initialize(self, executor, app): + super(EvaluationPlaneDisabledHandler, self).initialize(app) + self.executor = executor + + @gen.coroutine + def post(self): + if self.should_fail_with_auth_error() != AuthErrorStates.NONE: + self.fail_with_auth_error() + return + self.error_out(404, "Ad-hoc scripts have been disabled on this analytics extension, please contact your " + "administrator.") + + class EvaluationPlaneHandler(BaseHandler): """ EvaluationPlaneHandler is responsible for running arbitrary python scripts. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tabpy/tabpy_server/handlers/query_plane_handler.py new/TabPy-2.5.0/tabpy/tabpy_server/handlers/query_plane_handler.py --- old/TabPy-2.3.1/tabpy/tabpy_server/handlers/query_plane_handler.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tabpy/tabpy_server/handlers/query_plane_handler.py 2022-01-20 22:20:43.000000000 +0100 @@ -104,7 +104,7 @@ elif response_type == QueryError: self.error_out(400, "QueryError", info=response) else: - self.error_out(500, "Error querying GLS", info=response) + self.error_out(500, f"Error querying function '{po_name}'", info=response) return (None, None) @@ -149,7 +149,7 @@ # endpoint_name) is None if not po_name: self.error_out( - 404, "UnknownURI", info=f'Endpoint "{endpoint_name}" does not exist' + 404, "UnknownURI", info=f"Endpoint '{endpoint_name}' does not exist" ) return Binary files old/TabPy-2.3.1/tabpy/tabpy_server/static/TabPy_logo.png and new/TabPy-2.5.0/tabpy/tabpy_server/static/TabPy_logo.png differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tests/unit/server_tests/test_endpoint_handler.py new/TabPy-2.5.0/tests/unit/server_tests/test_endpoint_handler.py --- old/TabPy-2.3.1/tests/unit/server_tests/test_endpoint_handler.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tests/unit/server_tests/test_endpoint_handler.py 2022-01-20 22:20:43.000000000 +0100 @@ -161,4 +161,4 @@ ) }, ) - self.assertEqual(400, response.code) + self.assertEqual(406, response.code) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tests/unit/server_tests/test_endpoints_handler.py new/TabPy-2.5.0/tests/unit/server_tests/test_endpoints_handler.py --- old/TabPy-2.3.1/tests/unit/server_tests/test_endpoints_handler.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tests/unit/server_tests/test_endpoints_handler.py 2022-01-20 22:20:43.000000000 +0100 @@ -143,4 +143,4 @@ ) }, ) - self.assertEqual(400, response.code) + self.assertEqual(406, response.code) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tests/unit/server_tests/test_evaluation_plane_handler.py new/TabPy-2.5.0/tests/unit/server_tests/test_evaluation_plane_handler.py --- old/TabPy-2.3.1/tests/unit/server_tests/test_evaluation_plane_handler.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tests/unit/server_tests/test_evaluation_plane_handler.py 2022-01-20 22:20:43.000000000 +0100 @@ -2,16 +2,16 @@ import os import tempfile -from argparse import Namespace +from tornado.testing import AsyncHTTPTestCase + from tabpy.tabpy_server.app.app import TabPyApp from tabpy.tabpy_server.handlers.util import hash_password -from tornado.testing import AsyncHTTPTestCase -class TestEvaluationPlainHandlerWithAuth(AsyncHTTPTestCase): +class TestEvaluationPlaneHandlerWithAuth(AsyncHTTPTestCase): @classmethod def setUpClass(cls): - prefix = "__TestEvaluationPlainHandlerWithAuth_" + prefix = "__TestEvaluationPlaneHandlerWithAuth_" # create password file cls.pwd_file = tempfile.NamedTemporaryFile( mode="w+t", prefix=prefix, suffix=".txt", delete=False @@ -205,10 +205,10 @@ self.assertEqual(b'null', response.body) -class TestEvaluationPlainHandlerWithoutAuth(AsyncHTTPTestCase): +class TestEvaluationPlaneHandlerWithoutAuth(AsyncHTTPTestCase): @classmethod def setUpClass(cls): - prefix = "__TestEvaluationPlainHandlerWithoutAuth_" + prefix = "__TestEvaluationPlaneHandlerWithoutAuth_" # create state.ini dir and file cls.state_dir = tempfile.mkdtemp(prefix=prefix) @@ -285,4 +285,212 @@ ) }, ) - self.assertEqual(400, response.code) + self.assertEqual(406, response.code) + + +class TestEvaluationPlaneHandlerDisabledWithoutAuth(AsyncHTTPTestCase): + @classmethod + def setUpClass(cls): + prefix = "__TestEvaluationPlaneHandlerDisabledWithoutAuth_" + + # create config file + cls.config_file = tempfile.NamedTemporaryFile( + mode="w+t", prefix=prefix, suffix=".conf", delete=False + ) + cls.config_file.write( + "[TabPy]\n" + f"TABPY_EVALUATE_ENABLE = false" + ) + cls.config_file.close() + + cls.script = ( + '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},' + '"script":"res=[]\\nfor i in range(len(_arg1)):\\n ' + 'res.append(_arg1[i] * _arg2[i])\\nreturn res"}' + ) + + @classmethod + def tearDownClass(cls): + os.remove(cls.config_file.name) + + def get_app(self): + self.app = TabPyApp(self.config_file.name) + return self.app._create_tornado_web_app() + + def test_evaluation_disabled_fails(self): + response = self.fetch( + "/evaluate", + method="POST", + body=self.script + ) + self.assertEqual(404, response.code) + + +class TestEvaluationPlaneHandlerDisabledWithAuth(AsyncHTTPTestCase): + @classmethod + def setUpClass(cls): + prefix = "__TestEvaluationPlaneHandlerDisabledWithAuth_" + + # create password file + cls.pwd_file = tempfile.NamedTemporaryFile( + mode="w+t", prefix=prefix, suffix=".txt", delete=False + ) + username = "username" + password = "password" + cls.pwd_file.write(f"{username} {hash_password(username, password)}\n") + cls.pwd_file.close() + + # create state.ini dir and file + cls.state_dir = tempfile.mkdtemp(prefix=prefix) + cls.state_file = open(os.path.join(cls.state_dir, "state.ini"), "w+") + cls.state_file.write( + "[Service Info]\n" + "Name = TabPy Serve\n" + "Description = \n" + "Creation Time = 0\n" + "Access-Control-Allow-Origin = \n" + "Access-Control-Allow-Headers = \n" + "Access-Control-Allow-Methods = \n" + "\n" + "[Query Objects Service Versions]\n" + "\n" + "[Query Objects Docstrings]\n" + "\n" + "[Meta]\n" + "Revision Number = 1\n" + ) + cls.state_file.close() + + # create config file + cls.config_file = tempfile.NamedTemporaryFile( + mode="w+t", prefix=prefix, suffix=".conf", delete=False + ) + cls.config_file.write( + "[TabPy]\n" + f"TABPY_PWD_FILE = {cls.pwd_file.name}\n" + f"TABPY_STATE_PATH = {cls.state_dir}\n" + f"TABPY_EVALUATE_ENABLE = false" + ) + cls.config_file.close() + + cls.script = ( + '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},' + '"script":"res=[]\\nfor i in range(len(_arg1)):\\n ' + 'res.append(_arg1[i] * _arg2[i])\\nreturn res"}' + ) + + @classmethod + def tearDownClass(cls): + os.remove(cls.pwd_file.name) + os.remove(cls.state_file.name) + os.remove(cls.config_file.name) + os.rmdir(cls.state_dir) + + def get_app(self): + self.app = TabPyApp(self.config_file.name) + return self.app._create_tornado_web_app() + + def test_evaluation_disabled_fails_with_invalid_creds(self): + response = self.fetch( + "/evaluate", + method="POST", + body=self.script, + headers={ + "Authorization": "Basic {}".format( + base64.b64encode("user:wrong_password".encode("utf-8")).decode( + "utf-8" + ) + ) + }, + ) + self.assertEqual(401, response.code) + + def test_evaluation_disabled_fails_with_valid_creds(self): + response = self.fetch( + "/evaluate", + method="POST", + body=self.script, + headers={ + "Authorization": "Basic {}".format( + base64.b64encode("username:password".encode("utf-8")).decode( + "utf-8" + ) + ) + }, + ) + self.assertEqual(404, response.code) + + +class TestEvaluationPlaneHandlerEnabled(AsyncHTTPTestCase): + @classmethod + def setUpClass(cls): + prefix = "__TestEvaluationPlaneHandlerEnabled_" + + # create config file + cls.config_file = tempfile.NamedTemporaryFile( + mode="w+t", prefix=prefix, suffix=".conf", delete=False + ) + cls.config_file.write( + "[TabPy]\n" + f"TABPY_EVALUATE_ENABLE = true" + ) + cls.config_file.close() + + cls.script = ( + '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},' + '"script":"res=[]\\nfor i in range(len(_arg1)):\\n ' + 'res.append(_arg1[i] * _arg2[i])\\nreturn res"}' + ) + + @classmethod + def tearDownClass(cls): + os.remove(cls.config_file.name) + + def get_app(self): + self.app = TabPyApp(self.config_file.name) + return self.app._create_tornado_web_app() + + def test_evaluation_enabled(self): + response = self.fetch( + "/evaluate", + method="POST", + body=self.script + ) + self.assertEqual(200, response.code) + + +class TestEvaluationPlaneHandlerDefault(AsyncHTTPTestCase): + @classmethod + def setUpClass(cls): + prefix = "__TestEvaluationPlaneHandlerDefault_" + + # create config file + cls.config_file = tempfile.NamedTemporaryFile( + mode="w+t", prefix=prefix, suffix=".conf", delete=False + ) + cls.config_file.write( + "[TabPy]" + ) + cls.config_file.close() + + cls.script = ( + '{"data":{"_arg1":[2,3],"_arg2":[3,-1]},' + '"script":"res=[]\\nfor i in range(len(_arg1)):\\n ' + 'res.append(_arg1[i] * _arg2[i])\\nreturn res"}' + ) + + @classmethod + def tearDownClass(cls): + os.remove(cls.config_file.name) + + def get_app(self): + self.app = TabPyApp(self.config_file.name) + return self.app._create_tornado_web_app() + + def test_evaluation_default(self): + response = self.fetch( + "/evaluate", + method="POST", + body=self.script + ) + self.assertEqual(200, response.code) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/TabPy-2.3.1/tests/unit/server_tests/test_service_info_handler.py new/TabPy-2.5.0/tests/unit/server_tests/test_service_info_handler.py --- old/TabPy-2.3.1/tests/unit/server_tests/test_service_info_handler.py 2020-11-10 19:28:04.000000000 +0100 +++ new/TabPy-2.5.0/tests/unit/server_tests/test_service_info_handler.py 2022-01-20 22:20:43.000000000 +0100 @@ -14,7 +14,7 @@ "state_path": settings["state_file_path"], "server_version": settings[SettingsParameters.ServerVersion], "name": tabpy_state.name, - "versions": settings["versions"], + "versions": settings["versions"] } @@ -100,7 +100,7 @@ self.assertTrue("features" in v1) features = v1["features"] self.assertDictEqual( - {"authentication": {"methods": {"basic-auth": {}}, "required": True}}, + {"authentication": {"methods": {"basic-auth": {}}, "required": True}, 'evaluate_enabled': True}, features, ) @@ -126,7 +126,7 @@ v1 = versions["v1"] self.assertTrue("features" in v1) features = v1["features"] - self.assertDictEqual({}, features) + self.assertDictEqual({'evaluate_enabled': True}, features) def test_given_server_with_no_auth_and_password_expect_correct_info_response(self): header = { @@ -137,4 +137,4 @@ } response = self.fetch("/info", headers=header) - self.assertEqual(response.code, 400) + self.assertEqual(response.code, 406)