diff --git a/docs/docs/development.md b/docs/docs/development.md
index 18120587ca..dc30c5fe28 100644
--- a/docs/docs/development.md
+++ b/docs/docs/development.md
@@ -104,6 +104,12 @@ To install the Jupyterlab/Jupyter Notebook plugins from your local working
directory, simply install `python/perspective` with `pip` as you might normally
do.
+```bash
+(cd packages/perspective-jupyterlab && yarn run build)
+pip install -e python/perspective
+jupyter labextension develop python/perspective
+```
+
Afterwards, you should see it listed as a "local extension" when you run
`jupyter labextension list` and as a normal extension when you run
`jupyter nbextension list`.
diff --git a/python/perspective/perspective/__init__.py b/python/perspective/perspective/__init__.py
index 9ad4071b61..1ec1a36a82 100644
--- a/python/perspective/perspective/__init__.py
+++ b/python/perspective/perspective/__init__.py
@@ -17,3 +17,7 @@
from .handlers import *
from .nbextension import _jupyter_nbextension_paths
from .widget import *
+
+
+def _jupyter_labextension_paths():
+ return [{"src": "labextension", "dest": "@finos/perspective-jupyterlab"}]
diff --git a/python/perspective/perspective/templates/exported_widget.html.jinja b/python/perspective/perspective/templates/exported_widget.html.jinja
new file mode 100644
index 0000000000..9ba7dddb5c
--- /dev/null
+++ b/python/perspective/perspective/templates/exported_widget.html.jinja
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
diff --git a/python/perspective/perspective/widget/__init__.py b/python/perspective/perspective/widget/__init__.py
index 1b826a6a2f..0eba0dde2f 100644
--- a/python/perspective/perspective/widget/__init__.py
+++ b/python/perspective/perspective/widget/__init__.py
@@ -10,6 +10,7 @@
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
-from .widget import PerspectiveWidget
+from .widget import PerspectiveWidget, set_jupyter_html_export
-__all__ = ["PerspectiveWidget"]
+
+__all__ = ["PerspectiveWidget", "set_jupyter_html_export"]
diff --git a/python/perspective/perspective/widget/widget.py b/python/perspective/perspective/widget/widget.py
index 84751844ae..5c77866dc1 100644
--- a/python/perspective/perspective/widget/widget.py
+++ b/python/perspective/perspective/widget/widget.py
@@ -10,7 +10,10 @@
# ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+import base64
+import jinja2
import json
+import os
from datetime import date, datetime
from functools import partial
@@ -515,3 +518,39 @@ def _make_load_message(self):
return _PerspectiveWidgetMessage(-2, "table", msg_data)
else:
raise PerspectiveError("Widget does not have any data loaded - use the `load()` method to provide it with data.")
+
+ def _repr_mimebundle_(self, **kwargs):
+ super_bundle = super(DOMWidget, self)._repr_mimebundle_(**kwargs)
+ if not _jupyter_html_export_enabled():
+ return super_bundle
+ # Serialize viewer attrs + view data to be rendered in the template
+ viewer_attrs = self.save()
+ data = self.table.view().to_arrow()
+ b64_data = base64.encodebytes(data)
+
+ jinja_env = jinja2.Environment(
+ loader=jinja2.PackageLoader("perspective"),
+ autoescape=jinja2.select_autoescape(),
+ )
+ template = jinja_env.get_template("exported_widget.html.jinja")
+
+ return super(DOMWidget, self)._repr_mimebundle_(**kwargs) | {
+ "text/html": template.render(
+ psp_version=__version__,
+ viewer_id=self.model_id,
+ viewer_attrs=viewer_attrs,
+ b64_data=b64_data.decode("utf-8"),
+ )
+ }
+
+
+def _jupyter_html_export_enabled():
+ return os.environ.get("PSP_JUPYTER_HTML_EXPORT", None) == "1"
+
+
+def set_jupyter_html_export(val):
+ """Enables HTML export for Jupyter widgets, when set to True.
+ HTML export can also be enabled by setting the environment variable
+ `PSP_JUPYTER_HTML_EXPORT` to the string `1`.
+ """
+ os.environ["PSP_JUPYTER_HTML_EXPORT"] = "1" if val else "0"
diff --git a/python/perspective/requirements/requirements-310.txt b/python/perspective/requirements/requirements-310.txt
index f81bb29072..0fef0f72a9 100644
--- a/python/perspective/requirements/requirements-310.txt
+++ b/python/perspective/requirements/requirements-310.txt
@@ -1,161 +1,159 @@
+Babel==2.13.1
+Faker==20.0.3
+Jinja2==3.1.2
+MarkupSafe==2.1.3
+PyYAML==6.0.1
+Pygments==2.16.1
+Send2Trash==1.8.2
+Sphinx==7.2.6
aiofiles==22.1.0
-aiohttp==3.8.5
+aiohttp==3.8.6
aiosignal==1.3.1
aiosqlite==0.19.0
alabaster==0.7.13
-annotated-types==0.5.0
+annotated-types==0.6.0
anyio==3.7.1
appnope==0.1.3
-argon2-cffi==21.3.0
+argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
-arrow==1.2.3
-asttokens==2.2.1
-async-timeout==4.0.2
+arrow==1.3.0
+asttokens==2.4.1
+async-timeout==4.0.3
attrs==23.1.0
-Babel==2.12.1
-backcall==0.2.0
beautifulsoup4==4.12.2
black==23.1.0
-bleach==6.0.0
+bleach==6.1.0
certifi==2023.7.22
-cffi==1.15.1
-charset-normalizer==3.2.0
-click==8.1.6
-comm==0.1.4
-coverage==7.2.7
-debugpy==1.6.7
+cffi==1.16.0
+charset-normalizer==3.3.2
+click==8.1.7
+comm==0.2.0
+coverage==7.3.2
+debugpy==1.8.0
decorator==5.1.1
defusedxml==0.7.1
deprecation==2.1.0
docutils==0.20.1
entrypoints==0.4
-exceptiongroup==1.1.2
-executing==1.2.0
-Faker==19.2.0
-fastapi==0.100.1
-fastjsonschema==2.18.0
+executing==2.0.1
+fastapi==0.104.1
+fastjsonschema==2.19.0
flake8==6.1.0
flake8-black==0.3.6
fqdn==1.5.1
frozenlist==1.4.0
-future==0.18.3
h11==0.14.0
html5lib==1.1
-httpcore==0.17.3
-httpx==0.24.1
+httpcore==1.0.2
+httpx==0.25.1
idna==3.4
imagesize==1.4.1
-importlib-metadata==6.8.0
iniconfig==2.0.0
-ipykernel==6.25.0
-ipython==8.14.0
+ipykernel==6.26.0
+ipython==8.17.2
ipython-genutils==0.2.0
-ipywidgets==8.1.0
+ipywidgets==8.1.1
isoduration==20.11.0
-jedi==0.19.0
-Jinja2==3.1.2
+jedi==0.19.1
json5==0.9.14
jsonpointer==2.4
-jsonschema==4.18.6
-jsonschema-specifications==2023.7.1
-jupyter-events==0.7.0
+jsonschema==4.19.2
+jsonschema-specifications==2023.11.1
+jupyter-events==0.9.0
jupyter-ydoc==0.2.5
jupyter_client==7.4.9
-jupyter_core==5.3.1
+jupyter_core==5.5.0
jupyter_packaging==0.12.3
-jupyter_server==2.7.0
+jupyter_server==2.10.1
jupyter_server_fileid==0.9.0
jupyter_server_terminals==0.4.4
jupyter_server_ydoc==0.8.0
-jupyterlab==3.6.5
+jupyterlab==3.6.6
jupyterlab-pygments==0.2.2
-jupyterlab-widgets==3.0.8
-jupyterlab_server==2.24.0
-MarkupSafe==2.1.3
+jupyterlab-widgets==3.0.9
+jupyterlab_server==2.25.1
matplotlib-inline==0.1.6
mccabe==0.7.0
-mistune==3.0.1
+mistune==3.0.2
multidict==6.0.4
mypy-extensions==1.0.0
nbclassic==1.0.0
-nbclient==0.8.0
-nbconvert==7.7.3
+nbclient==0.9.0
+nbconvert==7.11.0
nbformat==5.9.2
-nest-asyncio==1.5.7
-notebook==6.5.5
+nest-asyncio==1.5.8
+notebook==6.5.6
notebook_shim==0.2.3
-numpy==1.25.2
-overrides==7.3.1
-packaging==23.1
-pandas==2.0.3
+numpy==1.26.2
+overrides==7.4.0
+packaging==23.2
+pandas==2.1.3
pandocfilters==1.5.0
parso==0.8.3
pathspec==0.11.2
+perspective-python==2.6.1
pexpect==4.8.0
-pickleshare==0.7.5
-platformdirs==3.10.0
-pluggy==1.2.0
-prometheus-client==0.17.1
-prompt-toolkit==3.0.39
-psutil==5.9.5
+pip==23.3.1
+platformdirs==4.0.0
+pluggy==1.3.0
+prometheus-client==0.18.0
+prompt-toolkit==3.0.41
+psutil==5.9.6
ptyprocess==0.7.0
pure-eval==0.2.2
-pyarrow==12.0.1
+pyarrow==14.0.1
pybind11==2.11.1
-pycodestyle==2.11.0
+pycodestyle==2.11.1
pycparser==2.21
-pydantic==2.1.1
-pydantic_core==2.4.0
+pydantic==2.5.1
+pydantic_core==2.14.3
pyflakes==3.1.0
-Pygments==2.15.1
-pytest==7.4.0
-pytest-aiohttp==1.0.4
+pytest==7.4.3
+pytest-aiohttp==1.0.5
pytest-asyncio==0.21.1
pytest-cov==4.1.0
pytest-tornado==0.8.1
pytest_check_links==0.9.0
python-dateutil==2.8.2
python-json-logger==2.0.7
-pytz==2023.3
-PyYAML==6.0.1
+pytz==2023.3.post1
pyzmq==24.0.1
-referencing==0.30.1
+referencing==0.31.0
requests==2.31.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
-rpds-py==0.9.2
-Send2Trash==1.8.2
+rpds-py==0.12.0
+setuptools==68.2.2
six==1.16.0
sniffio==1.3.0
snowballstemmer==2.2.0
-soupsieve==2.4.1
-Sphinx==7.1.2
-sphinx-markdown-builder==0.6.4
-sphinxcontrib-applehelp==1.0.4
-sphinxcontrib-devhelp==1.0.2
-sphinxcontrib-htmlhelp==2.0.1
+soupsieve==2.5
+sphinx-markdown-builder==0.6.5
+sphinxcontrib-applehelp==1.0.7
+sphinxcontrib-devhelp==1.0.5
+sphinxcontrib-htmlhelp==2.0.4
sphinxcontrib-jsmath==1.0.1
-sphinxcontrib-qthelp==1.0.3
-sphinxcontrib-serializinghtml==1.1.5
-stack-data==0.6.2
+sphinxcontrib-qthelp==1.0.6
+sphinxcontrib-serializinghtml==1.1.9
+stack-data==0.6.3
starlette==0.27.0
tabulate==0.9.0
-terminado==0.17.1
+terminado==0.18.0
tinycss2==1.2.1
-tomli==2.0.1
-tomlkit==0.12.1
-tornado==6.3.2
-traitlets==5.9.0
-typing_extensions==4.7.1
+tomlkit==0.12.3
+tornado==6.3.3
+traitlets==5.13.0
+types-python-dateutil==2.8.19.14
+typing_extensions==4.8.0
tzdata==2023.3
uri-template==1.3.0
-urllib3==2.0.4
-wcwidth==0.2.6
+urllib3==2.1.0
+wcwidth==0.2.10
webcolors==1.13
webencodings==0.5.1
-websocket-client==1.6.1
-widgetsnbextension==4.0.8
-y-py==0.6.0
+websocket-client==1.6.4
+wheel==0.41.3
+widgetsnbextension==4.0.9
+y-py==0.6.2
yarl==1.9.2
ypy-websocket==0.8.4
-zipp==3.16.2
\ No newline at end of file
diff --git a/python/perspective/requirements/requirements-311.txt b/python/perspective/requirements/requirements-311.txt
index 7c31745b6f..4ea5e95f18 100644
--- a/python/perspective/requirements/requirements-311.txt
+++ b/python/perspective/requirements/requirements-311.txt
@@ -1,157 +1,158 @@
+Babel==2.13.1
+Faker==20.0.3
+Jinja2==3.1.2
+MarkupSafe==2.1.3
+PyYAML==6.0.1
+Pygments==2.16.1
+Send2Trash==1.8.2
+Sphinx==7.2.6
aiofiles==22.1.0
-aiohttp==3.8.5
+aiohttp==3.8.6
aiosignal==1.3.1
aiosqlite==0.19.0
alabaster==0.7.13
-annotated-types==0.5.0
+annotated-types==0.6.0
anyio==3.7.1
appnope==0.1.3
-argon2-cffi==21.3.0
+argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
-arrow==1.2.3
-asttokens==2.2.1
-async-timeout==4.0.2
+arrow==1.3.0
+asttokens==2.4.1
+async-timeout==4.0.3
attrs==23.1.0
-Babel==2.12.1
-backcall==0.2.0
beautifulsoup4==4.12.2
black==23.1.0
-bleach==6.0.0
+bleach==6.1.0
certifi==2023.7.22
-cffi==1.15.1
-charset-normalizer==3.2.0
-click==8.1.6
-comm==0.1.4
-coverage==7.2.7
-debugpy==1.6.7
+cffi==1.16.0
+charset-normalizer==3.3.2
+click==8.1.7
+comm==0.2.0
+coverage==7.3.2
+debugpy==1.8.0
decorator==5.1.1
defusedxml==0.7.1
deprecation==2.1.0
docutils==0.20.1
-entrypoints==0.4
-executing==1.2.0
-Faker==19.2.0
-fastapi==0.100.1
-fastjsonschema==2.18.0
+executing==2.0.1
+fastapi==0.104.1
+fastjsonschema==2.19.0
flake8==6.1.0
flake8-black==0.3.6
fqdn==1.5.1
frozenlist==1.4.0
-future==0.18.3
h11==0.14.0
html5lib==1.1
-httpcore==0.17.3
-httpx==0.24.1
+httpcore==1.0.2
+httpx==0.25.1
idna==3.4
imagesize==1.4.1
iniconfig==2.0.0
-ipykernel==6.25.0
-ipython==8.14.0
+ipykernel==6.26.0
+ipython==8.17.2
ipython-genutils==0.2.0
-ipywidgets==8.1.0
+ipywidgets==8.1.1
isoduration==20.11.0
-jedi==0.19.0
-Jinja2==3.1.2
+jedi==0.19.1
json5==0.9.14
jsonpointer==2.4
-jsonschema==4.18.6
-jsonschema-specifications==2023.7.1
-jupyter-events==0.7.0
+jsonschema==4.19.2
+jsonschema-specifications==2023.11.1
+jupyter-events==0.9.0
jupyter-ydoc==0.2.5
-jupyter_client==7.4.9
-jupyter_core==5.3.1
+jupyter_client==8.6.0
+jupyter_core==5.5.0
jupyter_packaging==0.12.3
-jupyter_server==2.7.0
+jupyter_server==2.10.1
jupyter_server_fileid==0.9.0
jupyter_server_terminals==0.4.4
jupyter_server_ydoc==0.8.0
-jupyterlab==3.6.5
+jupyterlab==3.6.6
jupyterlab-pygments==0.2.2
-jupyterlab-widgets==3.0.8
-jupyterlab_server==2.24.0
-MarkupSafe==2.1.3
+jupyterlab-widgets==3.0.9
+jupyterlab_server==2.25.1
matplotlib-inline==0.1.6
mccabe==0.7.0
-mistune==3.0.1
+mistune==3.0.2
multidict==6.0.4
mypy-extensions==1.0.0
nbclassic==1.0.0
-nbclient==0.8.0
-nbconvert==7.7.3
+nbclient==0.9.0
+nbconvert==7.11.0
nbformat==5.9.2
-nest-asyncio==1.5.7
-notebook==6.5.5
+nest-asyncio==1.5.8
+notebook==6.5.4
notebook_shim==0.2.3
-numpy==1.25.2
-overrides==7.3.1
-packaging==23.1
-pandas==2.0.3
+numpy==1.26.2
+overrides==7.4.0
+packaging==23.2
+pandas==2.1.3
pandocfilters==1.5.0
parso==0.8.3
pathspec==0.11.2
+perspective-python==2.6.1
pexpect==4.8.0
-pickleshare==0.7.5
-platformdirs==3.10.0
-pluggy==1.2.0
-prometheus-client==0.17.1
-prompt-toolkit==3.0.39
-psutil==5.9.5
+pip==23.3.1
+platformdirs==4.0.0
+pluggy==1.3.0
+prometheus-client==0.18.0
+prompt-toolkit==3.0.41
+psutil==5.9.6
ptyprocess==0.7.0
pure-eval==0.2.2
-pyarrow==12.0.1
+pyarrow==14.0.1
pybind11==2.11.1
-pycodestyle==2.11.0
+pycodestyle==2.11.1
pycparser==2.21
-pydantic==2.1.1
-pydantic_core==2.4.0
+pydantic==2.5.1
+pydantic_core==2.14.3
pyflakes==3.1.0
-Pygments==2.15.1
-pytest==7.4.0
-pytest-aiohttp==1.0.4
+pytest==7.4.3
+pytest-aiohttp==1.0.5
pytest-asyncio==0.21.1
pytest-cov==4.1.0
pytest-tornado==0.8.1
pytest_check_links==0.9.0
python-dateutil==2.8.2
python-json-logger==2.0.7
-pytz==2023.3
-PyYAML==6.0.1
-pyzmq==24.0.1
-referencing==0.30.1
+pytz==2023.3.post1
+pyzmq==25.1.1
+referencing==0.31.0
requests==2.31.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
-rpds-py==0.9.2
-Send2Trash==1.8.2
+rpds-py==0.12.0
+setuptools==68.2.2
six==1.16.0
sniffio==1.3.0
snowballstemmer==2.2.0
-soupsieve==2.4.1
-Sphinx==7.1.2
-sphinx-markdown-builder==0.6.4
-sphinxcontrib-applehelp==1.0.4
-sphinxcontrib-devhelp==1.0.2
-sphinxcontrib-htmlhelp==2.0.1
+soupsieve==2.5
+sphinx-markdown-builder==0.6.5
+sphinxcontrib-applehelp==1.0.7
+sphinxcontrib-devhelp==1.0.5
+sphinxcontrib-htmlhelp==2.0.4
sphinxcontrib-jsmath==1.0.1
-sphinxcontrib-qthelp==1.0.3
-sphinxcontrib-serializinghtml==1.1.5
-stack-data==0.6.2
+sphinxcontrib-qthelp==1.0.6
+sphinxcontrib-serializinghtml==1.1.9
+stack-data==0.6.3
starlette==0.27.0
tabulate==0.9.0
-terminado==0.17.1
+terminado==0.18.0
tinycss2==1.2.1
-tomlkit==0.12.1
-tornado==6.3.2
-traitlets==5.9.0
-typing_extensions==4.7.1
+tomlkit==0.12.3
+tornado==6.3.3
+traitlets==5.13.0
+types-python-dateutil==2.8.19.14
+typing_extensions==4.8.0
tzdata==2023.3
uri-template==1.3.0
-urllib3==2.0.4
-wcwidth==0.2.6
+urllib3==2.1.0
+wcwidth==0.2.10
webcolors==1.13
webencodings==0.5.1
-websocket-client==1.6.1
-widgetsnbextension==4.0.8
-y-py==0.6.0
+websocket-client==1.6.4
+wheel==0.41.3
+widgetsnbextension==4.0.9
+y-py==0.6.2
yarl==1.9.2
-ypy-websocket==0.8.4
\ No newline at end of file
+ypy-websocket==0.8.4
diff --git a/python/perspective/requirements/requirements-37.txt b/python/perspective/requirements/requirements-37.txt
index 9f69dc4144..2a32a71586 100644
--- a/python/perspective/requirements/requirements-37.txt
+++ b/python/perspective/requirements/requirements-37.txt
@@ -1,17 +1,11 @@
-Faker==18.13.0
-Jinja2==3.1.2
-MarkupSafe==2.1.3
-Pygments==2.15.1
-Send2Trash==1.8.2
-Sphinx==4.3.2
aiohttp==3.8.5
aiosignal==1.3.1
alabaster==0.7.13
annotated-types==0.5.0
anyio==3.7.1
appnope==0.1.3
-argon2-cffi==21.3.0
argon2-cffi-bindings==21.2.0
+argon2-cffi==21.3.0
async-timeout==4.0.2
asynctest==0.13.0
attrs==23.1.0
@@ -31,10 +25,11 @@ defusedxml==0.7.1
docutils==0.17.1
entrypoints==0.4
exceptiongroup==1.1.2
+Faker==18.13.0
fastapi==0.100.0
fastjsonschema==2.17.1
-flake8==5.0.4
flake8-black==0.3.6
+flake8==5.0.4
frozenlist==1.3.3
future==0.18.3
h11==0.14.0
@@ -47,19 +42,21 @@ importlib-metadata==4.2.0
importlib-resources==5.12.0
iniconfig==2.0.0
ipykernel==6.16.2
-ipython==7.34.0
ipython-genutils==0.2.0
+ipython==7.34.0
ipywidgets==8.0.7
jedi==0.18.2
+Jinja2==3.1.2
json5==0.9.14
jsonschema==4.17.3
jupyter-client==7.4.9
jupyter-core==4.12.0
jupyter-server==1.24.0
-jupyterlab==3.5.3
jupyterlab-pygments==0.2.2
jupyterlab-server==2.15.2
jupyterlab-widgets==3.0.8
+jupyterlab==3.5.3
+MarkupSafe==2.1.3
matplotlib-inline==0.1.6
mccabe==0.7.0
mistune==3.0.1
@@ -70,8 +67,8 @@ nbclient==0.7.4
nbconvert==7.6.0
nbformat==5.8.0
nest-asyncio==1.5.6
-notebook==6.5.4
notebook-shim==0.2.3
+notebook==6.5.4
numpy==1.21.6
pandas==1.3.5
pandocfilters==1.5.0
@@ -91,25 +88,28 @@ pyarrow==12.0.1
pybind11==2.11.1
pycodestyle==2.9.1
pycparser==2.21
-pydantic==2.0.3
pydantic-core==2.3.0
+pydantic==2.0.3
pyflakes==2.5.0
+Pygments==2.15.1
pyrsistent==0.19.3
-pytest==7.4.0
pytest-aiohttp==1.0.4
pytest-asyncio==0.21.1
pytest-check-links==0.8.0
pytest-cov==4.1.0
pytest-tornado==0.8.1
+pytest==7.4.0
python-dateutil==2.8.2
pytz==2023.3
pyzmq==25.1.0
requests==2.31.0
+Send2Trash==1.8.2
six==1.16.0
sniffio==1.3.0
snowballstemmer==2.2.0
soupsieve==2.4.1
sphinx-markdown-builder==0.6.3
+Sphinx==4.3.2
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
@@ -131,4 +131,4 @@ webencodings==0.5.1
websocket-client==1.6.1
widgetsnbextension==4.0.8
yarl==1.9.2
-zipp==3.15.0
\ No newline at end of file
+zipp==3.15.0
diff --git a/python/perspective/requirements/requirements-38.txt b/python/perspective/requirements/requirements-38.txt
index 461dc292d4..db91791041 100644
--- a/python/perspective/requirements/requirements-38.txt
+++ b/python/perspective/requirements/requirements-38.txt
@@ -1,163 +1,161 @@
+Babel==2.13.1
+Faker==20.0.3
+Jinja2==3.1.2
+MarkupSafe==2.1.3
+PyYAML==6.0.1
+Pygments==2.16.1
+Send2Trash==1.8.2
+Sphinx==7.1.2
aiofiles==22.1.0
-aiohttp==3.8.5
+aiohttp==3.8.6
aiosignal==1.3.1
aiosqlite==0.19.0
alabaster==0.7.13
-annotated-types==0.5.0
+annotated-types==0.6.0
anyio==3.7.1
appnope==0.1.3
-argon2-cffi==21.3.0
+argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
-arrow==1.2.3
-asttokens==2.2.1
-async-timeout==4.0.2
+arrow==1.3.0
+asttokens==2.4.1
+async-timeout==4.0.3
attrs==23.1.0
-Babel==2.12.1
backcall==0.2.0
beautifulsoup4==4.12.2
black==23.1.0
-bleach==6.0.0
+bleach==6.1.0
certifi==2023.7.22
-cffi==1.15.1
-charset-normalizer==3.2.0
-click==8.1.6
-comm==0.1.4
-coverage==7.2.7
-debugpy==1.6.7
+cffi==1.16.0
+charset-normalizer==3.3.2
+click==8.1.7
+comm==0.2.0
+coverage==7.3.2
+debugpy==1.8.0
decorator==5.1.1
defusedxml==0.7.1
deprecation==2.1.0
docutils==0.20.1
entrypoints==0.4
-exceptiongroup==1.1.2
-executing==1.2.0
-Faker==19.2.0
-fastapi==0.100.1
-fastjsonschema==2.18.0
-flake8==6.1.0
+executing==2.0.1
+fastapi==0.104.1
+fastjsonschema==2.19.0
+flake8==5.0.4
flake8-black==0.3.6
fqdn==1.5.1
frozenlist==1.4.0
-future==0.18.3
h11==0.14.0
html5lib==1.1
-httpcore==0.17.3
-httpx==0.24.1
+httpcore==1.0.2
+httpx==0.25.1
idna==3.4
imagesize==1.4.1
-importlib-metadata==6.8.0
-importlib-resources==6.0.0
iniconfig==2.0.0
-ipykernel==6.25.0
-ipython==8.12.2
+ipykernel==6.26.0
+ipython==8.12.3
ipython-genutils==0.2.0
-ipywidgets==8.1.0
+ipywidgets==8.1.1
isoduration==20.11.0
-jedi==0.19.0
-Jinja2==3.1.2
+jedi==0.19.1
json5==0.9.14
jsonpointer==2.4
-jsonschema==4.18.6
-jsonschema-specifications==2023.7.1
-jupyter-events==0.7.0
+jsonschema==4.19.2
+jsonschema-specifications==2023.11.1
+jupyter-events==0.9.0
jupyter-ydoc==0.2.5
jupyter_client==7.4.9
-jupyter_core==5.3.1
+jupyter_core==5.5.0
jupyter_packaging==0.12.3
-jupyter_server==2.7.0
+jupyter_server==2.10.1
jupyter_server_fileid==0.9.0
jupyter_server_terminals==0.4.4
jupyter_server_ydoc==0.8.0
-jupyterlab==3.6.5
+jupyterlab==3.6.6
jupyterlab-pygments==0.2.2
-jupyterlab-widgets==3.0.8
-jupyterlab_server==2.24.0
-MarkupSafe==2.1.3
+jupyterlab-widgets==3.0.9
+jupyterlab_server==2.25.1
matplotlib-inline==0.1.6
mccabe==0.7.0
-mistune==3.0.1
+mistune==3.0.2
multidict==6.0.4
mypy-extensions==1.0.0
nbclassic==1.0.0
-nbclient==0.8.0
-nbconvert==7.7.3
+nbclient==0.9.0
+nbconvert==7.11.0
nbformat==5.9.2
-nest-asyncio==1.5.7
-notebook==6.5.5
+nest-asyncio==1.5.8
+notebook==6.5.6
notebook_shim==0.2.3
numpy==1.24.4
-overrides==7.3.1
-packaging==23.1
+overrides==7.4.0
+packaging==23.2
pandas==2.0.3
pandocfilters==1.5.0
parso==0.8.3
pathspec==0.11.2
+perspective-python==2.6.1
pexpect==4.8.0
pickleshare==0.7.5
-pkgutil_resolve_name==1.3.10
-platformdirs==3.10.0
-pluggy==1.2.0
-prometheus-client==0.17.1
-prompt-toolkit==3.0.39
-psutil==5.9.5
+pip==23.3.1
+platformdirs==4.0.0
+pluggy==1.3.0
+prometheus-client==0.18.0
+prompt-toolkit==3.0.41
+psutil==5.9.6
ptyprocess==0.7.0
pure-eval==0.2.2
-pyarrow==12.0.1
+pyarrow==14.0.1
pybind11==2.11.1
-pycodestyle==2.11.0
+pycodestyle==2.9.1
pycparser==2.21
-pydantic==2.1.1
-pydantic_core==2.4.0
-pyflakes==3.1.0
-Pygments==2.15.1
-pytest==7.4.0
-pytest-aiohttp==1.0.4
+pydantic==2.5.1
+pydantic_core==2.14.3
+pyflakes==2.5.0
+pytest==7.4.3
+pytest-aiohttp==1.0.5
pytest-asyncio==0.21.1
pytest-cov==4.1.0
pytest-tornado==0.8.1
pytest_check_links==0.9.0
python-dateutil==2.8.2
python-json-logger==2.0.7
-pytz==2023.3
-PyYAML==6.0.1
+pytz==2023.3.post1
pyzmq==24.0.1
-referencing==0.30.1
+referencing==0.31.0
requests==2.31.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
-rpds-py==0.9.2
-Send2Trash==1.8.2
+rpds-py==0.12.0
+setuptools==68.2.2
six==1.16.0
sniffio==1.3.0
snowballstemmer==2.2.0
-soupsieve==2.4.1
-Sphinx==7.1.2
-sphinx-markdown-builder==0.6.4
+soupsieve==2.5
+sphinx-markdown-builder==0.6.5
sphinxcontrib-applehelp==1.0.4
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
-stack-data==0.6.2
+stack-data==0.6.3
starlette==0.27.0
tabulate==0.9.0
-terminado==0.17.1
+terminado==0.18.0
tinycss2==1.2.1
-tomli==2.0.1
-tomlkit==0.12.1
-tornado==6.3.2
-traitlets==5.9.0
-typing_extensions==4.7.1
+tomlkit==0.12.3
+tornado==6.3.3
+traitlets==5.13.0
+types-python-dateutil==2.8.19.14
+typing_extensions==4.8.0
tzdata==2023.3
uri-template==1.3.0
-urllib3==2.0.4
-wcwidth==0.2.6
+urllib3==2.1.0
+wcwidth==0.2.10
webcolors==1.13
webencodings==0.5.1
-websocket-client==1.6.1
-widgetsnbextension==4.0.8
-y-py==0.6.0
+websocket-client==1.6.4
+wheel==0.41.3
+widgetsnbextension==4.0.9
+y-py==0.6.2
yarl==1.9.2
ypy-websocket==0.8.4
-zipp==3.16.2
\ No newline at end of file
diff --git a/python/perspective/requirements/requirements-39.txt b/python/perspective/requirements/requirements-39.txt
index f81bb29072..0fef0f72a9 100644
--- a/python/perspective/requirements/requirements-39.txt
+++ b/python/perspective/requirements/requirements-39.txt
@@ -1,161 +1,159 @@
+Babel==2.13.1
+Faker==20.0.3
+Jinja2==3.1.2
+MarkupSafe==2.1.3
+PyYAML==6.0.1
+Pygments==2.16.1
+Send2Trash==1.8.2
+Sphinx==7.2.6
aiofiles==22.1.0
-aiohttp==3.8.5
+aiohttp==3.8.6
aiosignal==1.3.1
aiosqlite==0.19.0
alabaster==0.7.13
-annotated-types==0.5.0
+annotated-types==0.6.0
anyio==3.7.1
appnope==0.1.3
-argon2-cffi==21.3.0
+argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
-arrow==1.2.3
-asttokens==2.2.1
-async-timeout==4.0.2
+arrow==1.3.0
+asttokens==2.4.1
+async-timeout==4.0.3
attrs==23.1.0
-Babel==2.12.1
-backcall==0.2.0
beautifulsoup4==4.12.2
black==23.1.0
-bleach==6.0.0
+bleach==6.1.0
certifi==2023.7.22
-cffi==1.15.1
-charset-normalizer==3.2.0
-click==8.1.6
-comm==0.1.4
-coverage==7.2.7
-debugpy==1.6.7
+cffi==1.16.0
+charset-normalizer==3.3.2
+click==8.1.7
+comm==0.2.0
+coverage==7.3.2
+debugpy==1.8.0
decorator==5.1.1
defusedxml==0.7.1
deprecation==2.1.0
docutils==0.20.1
entrypoints==0.4
-exceptiongroup==1.1.2
-executing==1.2.0
-Faker==19.2.0
-fastapi==0.100.1
-fastjsonschema==2.18.0
+executing==2.0.1
+fastapi==0.104.1
+fastjsonschema==2.19.0
flake8==6.1.0
flake8-black==0.3.6
fqdn==1.5.1
frozenlist==1.4.0
-future==0.18.3
h11==0.14.0
html5lib==1.1
-httpcore==0.17.3
-httpx==0.24.1
+httpcore==1.0.2
+httpx==0.25.1
idna==3.4
imagesize==1.4.1
-importlib-metadata==6.8.0
iniconfig==2.0.0
-ipykernel==6.25.0
-ipython==8.14.0
+ipykernel==6.26.0
+ipython==8.17.2
ipython-genutils==0.2.0
-ipywidgets==8.1.0
+ipywidgets==8.1.1
isoduration==20.11.0
-jedi==0.19.0
-Jinja2==3.1.2
+jedi==0.19.1
json5==0.9.14
jsonpointer==2.4
-jsonschema==4.18.6
-jsonschema-specifications==2023.7.1
-jupyter-events==0.7.0
+jsonschema==4.19.2
+jsonschema-specifications==2023.11.1
+jupyter-events==0.9.0
jupyter-ydoc==0.2.5
jupyter_client==7.4.9
-jupyter_core==5.3.1
+jupyter_core==5.5.0
jupyter_packaging==0.12.3
-jupyter_server==2.7.0
+jupyter_server==2.10.1
jupyter_server_fileid==0.9.0
jupyter_server_terminals==0.4.4
jupyter_server_ydoc==0.8.0
-jupyterlab==3.6.5
+jupyterlab==3.6.6
jupyterlab-pygments==0.2.2
-jupyterlab-widgets==3.0.8
-jupyterlab_server==2.24.0
-MarkupSafe==2.1.3
+jupyterlab-widgets==3.0.9
+jupyterlab_server==2.25.1
matplotlib-inline==0.1.6
mccabe==0.7.0
-mistune==3.0.1
+mistune==3.0.2
multidict==6.0.4
mypy-extensions==1.0.0
nbclassic==1.0.0
-nbclient==0.8.0
-nbconvert==7.7.3
+nbclient==0.9.0
+nbconvert==7.11.0
nbformat==5.9.2
-nest-asyncio==1.5.7
-notebook==6.5.5
+nest-asyncio==1.5.8
+notebook==6.5.6
notebook_shim==0.2.3
-numpy==1.25.2
-overrides==7.3.1
-packaging==23.1
-pandas==2.0.3
+numpy==1.26.2
+overrides==7.4.0
+packaging==23.2
+pandas==2.1.3
pandocfilters==1.5.0
parso==0.8.3
pathspec==0.11.2
+perspective-python==2.6.1
pexpect==4.8.0
-pickleshare==0.7.5
-platformdirs==3.10.0
-pluggy==1.2.0
-prometheus-client==0.17.1
-prompt-toolkit==3.0.39
-psutil==5.9.5
+pip==23.3.1
+platformdirs==4.0.0
+pluggy==1.3.0
+prometheus-client==0.18.0
+prompt-toolkit==3.0.41
+psutil==5.9.6
ptyprocess==0.7.0
pure-eval==0.2.2
-pyarrow==12.0.1
+pyarrow==14.0.1
pybind11==2.11.1
-pycodestyle==2.11.0
+pycodestyle==2.11.1
pycparser==2.21
-pydantic==2.1.1
-pydantic_core==2.4.0
+pydantic==2.5.1
+pydantic_core==2.14.3
pyflakes==3.1.0
-Pygments==2.15.1
-pytest==7.4.0
-pytest-aiohttp==1.0.4
+pytest==7.4.3
+pytest-aiohttp==1.0.5
pytest-asyncio==0.21.1
pytest-cov==4.1.0
pytest-tornado==0.8.1
pytest_check_links==0.9.0
python-dateutil==2.8.2
python-json-logger==2.0.7
-pytz==2023.3
-PyYAML==6.0.1
+pytz==2023.3.post1
pyzmq==24.0.1
-referencing==0.30.1
+referencing==0.31.0
requests==2.31.0
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
-rpds-py==0.9.2
-Send2Trash==1.8.2
+rpds-py==0.12.0
+setuptools==68.2.2
six==1.16.0
sniffio==1.3.0
snowballstemmer==2.2.0
-soupsieve==2.4.1
-Sphinx==7.1.2
-sphinx-markdown-builder==0.6.4
-sphinxcontrib-applehelp==1.0.4
-sphinxcontrib-devhelp==1.0.2
-sphinxcontrib-htmlhelp==2.0.1
+soupsieve==2.5
+sphinx-markdown-builder==0.6.5
+sphinxcontrib-applehelp==1.0.7
+sphinxcontrib-devhelp==1.0.5
+sphinxcontrib-htmlhelp==2.0.4
sphinxcontrib-jsmath==1.0.1
-sphinxcontrib-qthelp==1.0.3
-sphinxcontrib-serializinghtml==1.1.5
-stack-data==0.6.2
+sphinxcontrib-qthelp==1.0.6
+sphinxcontrib-serializinghtml==1.1.9
+stack-data==0.6.3
starlette==0.27.0
tabulate==0.9.0
-terminado==0.17.1
+terminado==0.18.0
tinycss2==1.2.1
-tomli==2.0.1
-tomlkit==0.12.1
-tornado==6.3.2
-traitlets==5.9.0
-typing_extensions==4.7.1
+tomlkit==0.12.3
+tornado==6.3.3
+traitlets==5.13.0
+types-python-dateutil==2.8.19.14
+typing_extensions==4.8.0
tzdata==2023.3
uri-template==1.3.0
-urllib3==2.0.4
-wcwidth==0.2.6
+urllib3==2.1.0
+wcwidth==0.2.10
webcolors==1.13
webencodings==0.5.1
-websocket-client==1.6.1
-widgetsnbextension==4.0.8
-y-py==0.6.0
+websocket-client==1.6.4
+wheel==0.41.3
+widgetsnbextension==4.0.9
+y-py==0.6.2
yarl==1.9.2
ypy-websocket==0.8.4
-zipp==3.16.2
\ No newline at end of file
diff --git a/python/perspective/setup.py b/python/perspective/setup.py
index 6c306ebd38..589bbca39c 100644
--- a/python/perspective/setup.py
+++ b/python/perspective/setup.py
@@ -56,6 +56,7 @@
########################
# Get requirement info #
requires = [
+ "Jinja2>=2.0,<4",
"ipywidgets>=7.5.1,<9",
"numpy>=1.21.6,<2",
"pandas>=0.22.0,<3",
diff --git a/tools/perspective-scripts/update_python.mjs b/tools/perspective-scripts/update_python.mjs
index 4161b12092..e710c64a21 100644
--- a/tools/perspective-scripts/update_python.mjs
+++ b/tools/perspective-scripts/update_python.mjs
@@ -28,22 +28,27 @@ const VERSIONS = [
for (const version of VERSIONS) {
sh`
- pip3 install "python/perspective[dev]"
+ pip3 install "python/perspective[dev]"
--dry-run
--report=report.json
- --python-version=${version}
- --only-binary=:all:
- --platform=manylinux_2_12_x86_64
- --platform=manylinux_2_17_x86_64
- --ignore-installed
+ --python-version=${version}
+ --only-binary=:all:
+ --platform=manylinux_2_12_x86_64
+ --platform=manylinux_2_17_x86_64
+ --ignore-installed
--target=.
`.runSync();
const data = JSON.parse(fs.readFileSync("report.json"));
let output = "";
+ const sortedInstalls = data.install.toSorted((a, b) => {
+ if (a.metadata.name < b.metadata.name) return -1;
+ if (a.metadata.name > b.metadata.name) return 1;
+ return 0;
+ });
for (const {
metadata: { version, name },
- } of data.install) {
+ } of sortedInstalls) {
output += `${name}==${version}\n`;
}