diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4b3a22fcc2a..a5b65930821 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -30,7 +30,7 @@ jobs:
- name: Install Python
uses: actions/setup-python@v4
with:
- python-version: '3.8'
+ python-version: '3.11'
- name: Cache the .tox dir
uses: actions/cache@v3
with:
@@ -47,7 +47,7 @@ jobs:
- name: Install Python
uses: actions/setup-python@v4
with:
- python-version: '3.8'
+ python-version: '3.11'
- name: Cache the .tox dir
uses: actions/cache@v3
with:
@@ -82,7 +82,7 @@ jobs:
- name: Install Python
uses: actions/setup-python@v4
with:
- python-version: '3.8'
+ python-version: '3.11'
- name: Cache the .tox dir
uses: actions/cache@v3
with:
@@ -109,7 +109,7 @@ jobs:
- name: Install Python
uses: actions/setup-python@v4
with:
- python-version: '3.8'
+ python-version: '3.11'
- name: Cache the .tox dir
uses: actions/cache@v3
with:
@@ -152,7 +152,7 @@ jobs:
- name: Install Python
uses: actions/setup-python@v4
with:
- python-version: '3.8'
+ python-version: '3.11'
- name: Cache the .tox dir
uses: actions/cache@v3
with:
diff --git a/.python-version b/.python-version
index 89a1ad7ad3c..d4b278f0a7d 100644
--- a/.python-version
+++ b/.python-version
@@ -1 +1 @@
-3.8.12
+3.11.7
diff --git a/Dockerfile b/Dockerfile
index 213910faf00..8ff374cd2f7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,7 +15,7 @@ COPY h/static ./h/static
RUN yarn build
# Stage 2: Build the rest of the app using the build output from Stage 1.
-FROM python:3.8.12-alpine3.13
+FROM python:3.11.7-alpine3.19
LABEL maintainer="Hypothes.is Project and contributors"
# Install system build and runtime dependencies.
diff --git a/h/accounts/schemas.py b/h/accounts/schemas.py
index 5d75d3fb11e..81d74aa6d44 100644
--- a/h/accounts/schemas.py
+++ b/h/accounts/schemas.py
@@ -4,7 +4,7 @@
import colander
import deform
-from jinja2 import Markup
+from markupsafe import Markup
from h import i18n, models
from h.models.user import (
diff --git a/h/form.py b/h/form.py
index 6b47aa7fdb1..016f3d448ac 100644
--- a/h/form.py
+++ b/h/form.py
@@ -5,8 +5,8 @@
form templates in preference to the defaults.
"""
import deform
-import jinja2
import pyramid_jinja2
+from markupsafe import Markup
from pyramid import httpexceptions
from pyramid.path import AssetResolver
@@ -45,7 +45,7 @@ def __call__(self, template_name, **kwargs):
context = self._system.copy()
context.update(kwargs)
- return jinja2.Markup(template.render(context))
+ return Markup(template.render(context))
def create_environment(base):
diff --git a/h/presenters/annotation_html.py b/h/presenters/annotation_html.py
index 9bd38c286e8..0309fa9f5a5 100644
--- a/h/presenters/annotation_html.py
+++ b/h/presenters/annotation_html.py
@@ -1,5 +1,5 @@
-import jinja2
from dateutil import parser
+from markupsafe import Markup, escape
from h.presenters.document_html import DocumentHTMLPresenter
@@ -16,7 +16,7 @@ def __init__(self, annotation):
@property
def uri(self):
- return jinja2.escape(self.annotation.target_uri)
+ return escape(self.annotation.target_uri)
@property
def text_rendered(self):
@@ -28,15 +28,15 @@ def text_rendered(self):
care of all necessary escaping.
"""
if self.annotation.text_rendered:
- return jinja2.Markup(self.annotation.text_rendered)
- return jinja2.Markup("")
+ return Markup(self.annotation.text_rendered)
+ return Markup("")
@property
def quote(self):
"""Get the text in the document which this annotation refers to."""
selection = self._get_selection()
if selection:
- return jinja2.escape(selection)
+ return escape(selection)
return ""
@@ -55,12 +55,12 @@ def description(self):
selection = self._get_selection()
if selection:
- selection = jinja2.escape(selection)
+ selection = escape(selection)
description += f"<blockquote>{selection}</blockquote>"
text = self.annotation.text
if text:
- text = jinja2.escape(text)
+ text = escape(text)
description += f"{text}"
return description
@@ -74,7 +74,7 @@ def created_day_string(self):
date.
"""
- created_string = jinja2.escape(self.annotation.created)
+ created_string = escape(self.annotation.created)
return parser.parse(created_string).strftime("%Y-%m-%d")
@property
diff --git a/h/presenters/document_html.py b/h/presenters/document_html.py
index f6864d27805..fd8f1ec0074 100644
--- a/h/presenters/document_html.py
+++ b/h/presenters/document_html.py
@@ -1,6 +1,6 @@
from urllib.parse import unquote, urlparse
-import jinja2
+import markupsafe
class DocumentHTMLPresenter:
@@ -24,7 +24,7 @@ def filename(self):
"""
if self.uri.lower().startswith("file:///"):
- return jinja2.escape(self.uri.split("/")[-1])
+ return markupsafe.escape(self.uri.split("/")[-1])
return ""
@property
@@ -44,7 +44,7 @@ def href(self):
"""
if self.document.web_uri:
- return jinja2.escape(self.document.web_uri)
+ return markupsafe.escape(self.document.web_uri)
return ""
@property
@@ -64,14 +64,14 @@ def hostname_or_filename(self):
object so that it doesn't get double-escaped.
"""
if self.filename:
- return jinja2.escape(unquote(self.filename))
+ return markupsafe.escape(unquote(self.filename))
hostname = urlparse(self.uri).hostname
# urlparse()'s .hostname is sometimes None.
hostname = hostname or ""
- return jinja2.escape(hostname)
+ return markupsafe.escape(hostname)
@property
def link(self):
@@ -129,7 +129,7 @@ def link_text(self):
Markup object so it doesn't get double-escaped.
"""
- title = jinja2.escape(self.title)
+ title = markupsafe.escape(self.title)
# Sometimes self.title is the annotated document's URI (if the document
# has no title). In those cases we want to remove the http(s):// from
@@ -160,17 +160,17 @@ def title(self):
# Convert non-string titles into strings.
# We're assuming that title cannot be a byte string.
title = str(title)
- return jinja2.escape(title)
+ return markupsafe.escape(title)
if self.filename:
- return jinja2.escape(unquote(self.filename))
+ return markupsafe.escape(unquote(self.filename))
- return jinja2.escape(unquote(self.uri))
+ return markupsafe.escape(unquote(self.uri))
@property
def uri(self):
if self.document.document_uris:
- return jinja2.escape(self.document.document_uris[0].uri)
+ return markupsafe.escape(self.document.document_uris[0].uri)
return ""
@property
@@ -205,7 +205,7 @@ def truncate(content, length=55):
if len(content) <= length:
return content
- return content[:length] + jinja2.Markup("…")
+ return content[:length] + markupsafe.Markup("…")
host_or_filename = truncate(host_or_filename)
link_text = truncate(link_text)
@@ -220,10 +220,10 @@ def truncate(content, length=55):
link += "
{host_or_filename}"
link = link.format(
- href=jinja2.escape(href),
- title=jinja2.escape(title),
- link_text=jinja2.escape(link_text),
- host_or_filename=jinja2.escape(host_or_filename),
+ href=markupsafe.escape(href),
+ title=markupsafe.escape(title),
+ link_text=markupsafe.escape(link_text),
+ host_or_filename=markupsafe.escape(host_or_filename),
)
- return jinja2.Markup(link)
+ return markupsafe.Markup(link)
diff --git a/h/views/accounts.py b/h/views/accounts.py
index 5d3abfbf1e2..b368c3c5463 100644
--- a/h/views/accounts.py
+++ b/h/views/accounts.py
@@ -4,7 +4,7 @@
import colander
import deform
-import jinja2
+from markupsafe import Markup
from pyramid import httpexceptions, security
from pyramid.exceptions import BadCSRFToken
from pyramid.view import view_config, view_defaults
@@ -285,7 +285,7 @@ def _reset_password(self, user, password):
svc.update_password(user, password)
self.request.session.flash(
- jinja2.Markup(
+ Markup(
_(
"Your password has been reset. You can now log in with "
"your new password."
@@ -321,7 +321,7 @@ def get_when_not_logged_in(self):
activation = models.Activation.get_by_code(self.request.db, code)
if activation is None:
self.request.session.flash(
- jinja2.Markup(
+ Markup(
_(
"We didn't recognize that activation link. "
"Have you already activated your account? "
@@ -340,7 +340,7 @@ def get_when_not_logged_in(self):
user.activate()
self.request.session.flash(
- jinja2.Markup(
+ Markup(
_(
"Your account has been activated! "
"You can now log in using the password you provided."
@@ -369,14 +369,12 @@ def get_when_logged_in(self):
# The user is already logged in to the account (so the account
# must already be activated).
self.request.session.flash(
- jinja2.Markup(
- _("Your account has been activated and you're logged in.")
- ),
+ Markup(_("Your account has been activated and you're logged in.")),
"success",
)
else:
self.request.session.flash(
- jinja2.Markup(
+ Markup(
_(
"You're already logged in to a different account. "
'Log out and open the activation link '
diff --git a/h/views/activity.py b/h/views/activity.py
index 0041bd5d5e8..6ad5e426f6d 100644
--- a/h/views/activity.py
+++ b/h/views/activity.py
@@ -2,7 +2,7 @@
from urllib.parse import urlparse
-from jinja2 import Markup
+from markupsafe import Markup
from pyramid import httpexceptions
from pyramid.view import view_config, view_defaults
diff --git a/h/views/admin/groups.py b/h/views/admin/groups.py
index e1a0c18b32e..f8719d94339 100644
--- a/h/views/admin/groups.py
+++ b/h/views/admin/groups.py
@@ -1,4 +1,4 @@
-from jinja2 import Markup
+from markupsafe import Markup
from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config, view_defaults
diff --git a/h/views/admin/organizations.py b/h/views/admin/organizations.py
index 8217e1098fa..07c292e6496 100644
--- a/h/views/admin/organizations.py
+++ b/h/views/admin/organizations.py
@@ -1,4 +1,4 @@
-from jinja2 import Markup
+from markupsafe import Markup
from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config, view_defaults
from sqlalchemy import func
diff --git a/h/views/admin/users.py b/h/views/admin/users.py
index 91fe83d956e..08ddd8eb161 100644
--- a/h/views/admin/users.py
+++ b/h/views/admin/users.py
@@ -1,4 +1,4 @@
-import jinja2
+from markupsafe import Markup
from pyramid import httpexceptions
from pyramid.view import view_config
@@ -70,7 +70,7 @@ def users_activate(request):
request.session.flash(
# pylint:disable=consider-using-f-string
- jinja2.Markup(_("User {name} has been activated!".format(name=user.username))),
+ Markup(_("User {name} has been activated!".format(name=user.username))),
"success",
)
@@ -145,7 +145,7 @@ def users_delete(request):
@view_config(context=UserNotFoundError)
def user_not_found(exc, request): # pragma: no cover
- request.session.flash(jinja2.Markup(_(exc.message)), "error")
+ request.session.flash(Markup(_(exc.message)), "error")
return httpexceptions.HTTPFound(location=request.route_path("admin.users"))
diff --git a/requirements/checkdocs.txt b/requirements/checkdocs.txt
index 304230e460f..fc07073a116 100644
--- a/requirements/checkdocs.txt
+++ b/requirements/checkdocs.txt
@@ -1,10 +1,10 @@
#
-# This file is autogenerated by pip-compile with Python 3.8
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile requirements/checkdocs.in
#
-alabaster==0.7.13
+alabaster==0.7.16
# via sphinx
babel==2.14.0
# via sphinx
@@ -27,15 +27,12 @@ idna==3.6
imagesize==1.4.1
# via sphinx
importlib-metadata==7.0.1
- # via
- # build
- # pip-sync-faster
- # sphinx
+ # via pip-sync-faster
jinja2==3.1.3
# via sphinx
livereload==2.6.3
# via sphinx-autobuild
-markupsafe==2.0.1
+markupsafe==2.1.3
# via jinja2
packaging==23.2
# via
@@ -51,43 +48,41 @@ pygments==2.17.2
# via sphinx
pyproject-hooks==1.0.0
# via build
-pytz==2023.3.post1
- # via babel
requests==2.31.0
# via sphinx
six==1.16.0
# via livereload
snowballstemmer==2.2.0
# via sphinx
-sphinx==7.1.2
+sphinx==7.2.6
# via
# -r requirements/checkdocs.in
# sphinx-autobuild
# sphinx-rtd-theme
+ # sphinxcontrib-applehelp
+ # sphinxcontrib-devhelp
+ # sphinxcontrib-htmlhelp
# sphinxcontrib-jquery
+ # sphinxcontrib-qthelp
+ # sphinxcontrib-serializinghtml
sphinx-autobuild==2021.3.14
# via -r requirements/checkdocs.in
sphinx-rtd-theme==2.0.0
# via -r requirements/checkdocs.in
-sphinxcontrib-applehelp==1.0.4
+sphinxcontrib-applehelp==1.0.7
# via sphinx
-sphinxcontrib-devhelp==1.0.2
+sphinxcontrib-devhelp==1.0.5
# via sphinx
-sphinxcontrib-htmlhelp==2.0.1
+sphinxcontrib-htmlhelp==2.0.4
# via sphinx
sphinxcontrib-jquery==4.1
# via sphinx-rtd-theme
sphinxcontrib-jsmath==1.0.1
# via sphinx
-sphinxcontrib-qthelp==1.0.3
+sphinxcontrib-qthelp==1.0.6
# via sphinx
-sphinxcontrib-serializinghtml==1.1.5
+sphinxcontrib-serializinghtml==1.1.9
# via sphinx
-tomli==2.0.1
- # via
- # build
- # pip-tools
- # pyproject-hooks
tornado==6.4
# via livereload
urllib3==2.1.0
diff --git a/requirements/checkformatting.txt b/requirements/checkformatting.txt
index 1b0f87bcd29..ccfac48215b 100644
--- a/requirements/checkformatting.txt
+++ b/requirements/checkformatting.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.8
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile requirements/checkformatting.in
@@ -13,9 +13,7 @@ click==8.1.7
# black
# pip-tools
importlib-metadata==7.0.1
- # via
- # build
- # pip-sync-faster
+ # via pip-sync-faster
isort==5.13.2
# via -r requirements/checkformatting.in
mypy-extensions==1.0.0
@@ -36,14 +34,6 @@ platformdirs==4.1.0
# via black
pyproject-hooks==1.0.0
# via build
-tomli==2.0.1
- # via
- # black
- # build
- # pip-tools
- # pyproject-hooks
-typing-extensions==4.9.0
- # via black
wheel==0.42.0
# via pip-tools
zipp==3.17.0
diff --git a/requirements/coverage.txt b/requirements/coverage.txt
index 50eb288ed34..ad907c2bd0c 100644
--- a/requirements/coverage.txt
+++ b/requirements/coverage.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.8
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile requirements/coverage.in
@@ -11,9 +11,7 @@ click==8.1.7
coverage==7.4.0
# via -r requirements/coverage.in
importlib-metadata==7.0.1
- # via
- # build
- # pip-sync-faster
+ # via pip-sync-faster
packaging==23.2
# via build
pip-sync-faster==0.0.3
@@ -24,11 +22,6 @@ pip-tools==7.3.0
# pip-sync-faster
pyproject-hooks==1.0.0
# via build
-tomli==2.0.1
- # via
- # build
- # pip-tools
- # pyproject-hooks
wheel==0.42.0
# via pip-tools
zipp==3.17.0
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 6acd6be9c2e..417b966d188 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.8
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile requirements/dev.in
@@ -17,14 +17,6 @@ attrs==23.2.0
# -r requirements/prod.txt
# jsonschema
# referencing
-backcall==0.2.0
- # via ipython
-backports-zoneinfo[tzdata]==0.2.1
- # via
- # -r requirements/prod.txt
- # backports-zoneinfo
- # celery
- # kombu
bcrypt==4.1.2
# via -r requirements/prod.txt
billiard==4.2.0
@@ -125,26 +117,18 @@ hupper==1.12
importlib-metadata==7.0.1
# via
# -r requirements/prod.txt
- # alembic
- # build
- # chameleon
# data-tasks
# h-api
# h-assets
- # markdown
# pip-sync-faster
importlib-resources==6.1.1
# via
# -r requirements/prod.txt
- # alembic
- # chameleon
# data-tasks
# h-api
- # jsonschema
- # jsonschema-specifications
ipdb==0.13.13
# via -r requirements/dev.in
-ipython==8.12.3
+ipython==8.20.0
# via
# ipdb
# pyramid-ipython
@@ -157,7 +141,7 @@ itsdangerous==2.1.2
# via -r requirements/prod.txt
jedi==0.19.1
# via ipython
-jinja2==2.11.3
+jinja2==3.1.3
# via
# -r requirements/prod.txt
# data-tasks
@@ -180,7 +164,7 @@ mako==1.3.0
# alembic
markdown==3.5.2
# via -r requirements/prod.txt
-markupsafe==1.1.1
+markupsafe==2.1.3
# via
# -r requirements/prod.txt
# jinja2
@@ -212,18 +196,12 @@ peppercorn==0.6
# deform
pexpect==4.9.0
# via ipython
-pickleshare==0.7.5
- # via ipython
pip-sync-faster==0.0.3
# via -r requirements/dev.in
pip-tools==7.3.0
# via
# -r requirements/dev.in
# pip-sync-faster
-pkgutil-resolve-name==1.3.10
- # via
- # -r requirements/prod.txt
- # jsonschema
plaster==1.1.2
# via
# -r requirements/prod.txt
@@ -366,12 +344,6 @@ text-unidecode==1.3
# via
# -r requirements/prod.txt
# python-slugify
-tomli==2.0.1
- # via
- # build
- # ipdb
- # pip-tools
- # pyproject-hooks
traitlets==5.14.1
# via
# ipython
@@ -393,13 +365,9 @@ typing-extensions==4.9.0
# via
# -r requirements/prod.txt
# alembic
- # faker
- # ipython
- # kombu
tzdata==2023.4
# via
# -r requirements/prod.txt
- # backports-zoneinfo
# celery
urllib3==2.1.0
# via
@@ -442,7 +410,6 @@ zipp==3.17.0
# via
# -r requirements/prod.txt
# importlib-metadata
- # importlib-resources
zope-deprecation==5.0
# via
# -r requirements/prod.txt
diff --git a/requirements/docs.txt b/requirements/docs.txt
index 41a52a99123..b55bde8e118 100644
--- a/requirements/docs.txt
+++ b/requirements/docs.txt
@@ -1,10 +1,10 @@
#
-# This file is autogenerated by pip-compile with Python 3.8
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile requirements/docs.in
#
-alabaster==0.7.13
+alabaster==0.7.16
# via sphinx
babel==2.14.0
# via sphinx
@@ -27,15 +27,12 @@ idna==3.6
imagesize==1.4.1
# via sphinx
importlib-metadata==7.0.1
- # via
- # build
- # pip-sync-faster
- # sphinx
+ # via pip-sync-faster
jinja2==3.1.3
# via sphinx
livereload==2.6.3
# via sphinx-autobuild
-markupsafe==2.0.1
+markupsafe==2.1.3
# via jinja2
packaging==23.2
# via
@@ -51,43 +48,41 @@ pygments==2.17.2
# via sphinx
pyproject-hooks==1.0.0
# via build
-pytz==2023.3.post1
- # via babel
requests==2.31.0
# via sphinx
six==1.16.0
# via livereload
snowballstemmer==2.2.0
# via sphinx
-sphinx==7.1.2
+sphinx==7.2.6
# via
# -r requirements/docs.in
# sphinx-autobuild
# sphinx-rtd-theme
+ # sphinxcontrib-applehelp
+ # sphinxcontrib-devhelp
+ # sphinxcontrib-htmlhelp
# sphinxcontrib-jquery
+ # sphinxcontrib-qthelp
+ # sphinxcontrib-serializinghtml
sphinx-autobuild==2021.3.14
# via -r requirements/docs.in
sphinx-rtd-theme==2.0.0
# via -r requirements/docs.in
-sphinxcontrib-applehelp==1.0.4
+sphinxcontrib-applehelp==1.0.7
# via sphinx
-sphinxcontrib-devhelp==1.0.2
+sphinxcontrib-devhelp==1.0.5
# via sphinx
-sphinxcontrib-htmlhelp==2.0.1
+sphinxcontrib-htmlhelp==2.0.4
# via sphinx
sphinxcontrib-jquery==4.1
# via sphinx-rtd-theme
sphinxcontrib-jsmath==1.0.1
# via sphinx
-sphinxcontrib-qthelp==1.0.3
+sphinxcontrib-qthelp==1.0.6
# via sphinx
-sphinxcontrib-serializinghtml==1.1.5
+sphinxcontrib-serializinghtml==1.1.9
# via sphinx
-tomli==2.0.1
- # via
- # build
- # pip-tools
- # pyproject-hooks
tornado==6.4
# via livereload
urllib3==2.1.0
diff --git a/requirements/format.txt b/requirements/format.txt
index f6bbd202a65..0cc48a38c28 100644
--- a/requirements/format.txt
+++ b/requirements/format.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.8
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile requirements/format.in
@@ -13,9 +13,7 @@ click==8.1.7
# black
# pip-tools
importlib-metadata==7.0.1
- # via
- # build
- # pip-sync-faster
+ # via pip-sync-faster
isort==5.13.2
# via -r requirements/format.in
mypy-extensions==1.0.0
@@ -36,14 +34,6 @@ platformdirs==4.1.0
# via black
pyproject-hooks==1.0.0
# via build
-tomli==2.0.1
- # via
- # black
- # build
- # pip-tools
- # pyproject-hooks
-typing-extensions==4.9.0
- # via black
wheel==0.42.0
# via pip-tools
zipp==3.17.0
diff --git a/requirements/functests.txt b/requirements/functests.txt
index 50bff1ce6c2..c424b36ffbb 100644
--- a/requirements/functests.txt
+++ b/requirements/functests.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.8
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile requirements/functests.in
@@ -15,12 +15,6 @@ attrs==23.2.0
# -r requirements/prod.txt
# jsonschema
# referencing
-backports-zoneinfo[tzdata]==0.2.1
- # via
- # -r requirements/prod.txt
- # backports-zoneinfo
- # celery
- # kombu
bcrypt==4.1.2
# via -r requirements/prod.txt
beautifulsoup4==4.12.2
@@ -89,8 +83,6 @@ elasticsearch==6.8.2
# elasticsearch-dsl
elasticsearch-dsl==6.4.0
# via -r requirements/prod.txt
-exceptiongroup==1.2.0
- # via pytest
factory-boy==3.3.0
# via -r requirements/functests.in
faker==22.2.0
@@ -119,23 +111,15 @@ hupper==1.12
importlib-metadata==7.0.1
# via
# -r requirements/prod.txt
- # alembic
- # build
- # chameleon
# data-tasks
# h-api
# h-assets
- # markdown
# pip-sync-faster
importlib-resources==6.1.1
# via
# -r requirements/prod.txt
- # alembic
- # chameleon
# data-tasks
# h-api
- # jsonschema
- # jsonschema-specifications
iniconfig==2.0.0
# via pytest
iso8601==2.1.0
@@ -145,7 +129,7 @@ iso8601==2.1.0
# deform
itsdangerous==2.1.2
# via -r requirements/prod.txt
-jinja2==2.11.3
+jinja2==3.1.3
# via
# -r requirements/prod.txt
# data-tasks
@@ -168,7 +152,7 @@ mako==1.3.0
# alembic
markdown==3.5.2
# via -r requirements/prod.txt
-markupsafe==1.1.1
+markupsafe==2.1.3
# via
# -r requirements/prod.txt
# jinja2
@@ -201,10 +185,6 @@ pip-tools==7.3.0
# via
# -r requirements/functests.in
# pip-sync-faster
-pkgutil-resolve-name==1.3.10
- # via
- # -r requirements/prod.txt
- # jsonschema
plaster==1.1.2
# via
# -r requirements/prod.txt
@@ -344,12 +324,6 @@ text-unidecode==1.3
# via
# -r requirements/prod.txt
# python-slugify
-tomli==2.0.1
- # via
- # build
- # pip-tools
- # pyproject-hooks
- # pytest
transaction==4.0
# via
# -r requirements/prod.txt
@@ -367,12 +341,9 @@ typing-extensions==4.9.0
# via
# -r requirements/prod.txt
# alembic
- # faker
- # kombu
tzdata==2023.4
# via
# -r requirements/prod.txt
- # backports-zoneinfo
# celery
urllib3==2.1.0
# via
@@ -420,7 +391,6 @@ zipp==3.17.0
# via
# -r requirements/prod.txt
# importlib-metadata
- # importlib-resources
zope-deprecation==5.0
# via
# -r requirements/prod.txt
diff --git a/requirements/lint.txt b/requirements/lint.txt
index 0c61729e859..ca3b66b2143 100644
--- a/requirements/lint.txt
+++ b/requirements/lint.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.8
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile requirements/lint.in
@@ -22,13 +22,6 @@ attrs==23.2.0
# hypothesis
# jsonschema
# referencing
-backports-zoneinfo[tzdata]==0.2.1
- # via
- # -r requirements/functests.txt
- # -r requirements/tests.txt
- # backports-zoneinfo
- # celery
- # kombu
bcrypt==4.1.2
# via
# -r requirements/functests.txt
@@ -133,12 +126,6 @@ elasticsearch-dsl==6.4.0
# via
# -r requirements/functests.txt
# -r requirements/tests.txt
-exceptiongroup==1.2.0
- # via
- # -r requirements/functests.txt
- # -r requirements/tests.txt
- # hypothesis
- # pytest
execnet==2.0.2
# via
# -r requirements/tests.txt
@@ -195,24 +182,16 @@ importlib-metadata==7.0.1
# via
# -r requirements/functests.txt
# -r requirements/tests.txt
- # alembic
- # build
- # chameleon
# data-tasks
# h-api
# h-assets
- # markdown
# pip-sync-faster
importlib-resources==6.1.1
# via
# -r requirements/functests.txt
# -r requirements/tests.txt
- # alembic
- # chameleon
# data-tasks
# h-api
- # jsonschema
- # jsonschema-specifications
iniconfig==2.0.0
# via
# -r requirements/functests.txt
@@ -230,7 +209,7 @@ itsdangerous==2.1.2
# via
# -r requirements/functests.txt
# -r requirements/tests.txt
-jinja2==2.11.3
+jinja2==3.1.3
# via
# -r requirements/functests.txt
# -r requirements/tests.txt
@@ -260,7 +239,7 @@ markdown==3.5.2
# via
# -r requirements/functests.txt
# -r requirements/tests.txt
-markupsafe==1.1.1
+markupsafe==2.1.3
# via
# -r requirements/functests.txt
# -r requirements/tests.txt
@@ -310,11 +289,6 @@ pip-tools==7.3.0
# -r requirements/lint.in
# -r requirements/tests.txt
# pip-sync-faster
-pkgutil-resolve-name==1.3.10
- # via
- # -r requirements/functests.txt
- # -r requirements/tests.txt
- # jsonschema
plaster==1.1.2
# via
# -r requirements/functests.txt
@@ -539,16 +513,6 @@ text-unidecode==1.3
# -r requirements/functests.txt
# -r requirements/tests.txt
# python-slugify
-tomli==2.0.1
- # via
- # -r requirements/functests.txt
- # -r requirements/tests.txt
- # build
- # coverage
- # pip-tools
- # pylint
- # pyproject-hooks
- # pytest
tomlkit==0.12.3
# via pylint
transaction==4.0
@@ -571,15 +535,10 @@ typing-extensions==4.9.0
# -r requirements/functests.txt
# -r requirements/tests.txt
# alembic
- # astroid
- # faker
- # kombu
- # pylint
tzdata==2023.4
# via
# -r requirements/functests.txt
# -r requirements/tests.txt
- # backports-zoneinfo
# celery
urllib3==2.1.0
# via
@@ -644,7 +603,6 @@ zipp==3.17.0
# -r requirements/functests.txt
# -r requirements/tests.txt
# importlib-metadata
- # importlib-resources
zope-deprecation==5.0
# via
# -r requirements/functests.txt
diff --git a/requirements/prod.in b/requirements/prod.in
index 1854bfb1c3e..4b48841caee 100644
--- a/requirements/prod.in
+++ b/requirements/prod.in
@@ -37,8 +37,8 @@ pycryptodomex
pyparsing >= 2.1.5
pyramid
pyramid-exclog
-markupsafe==1.1.1
-jinja2 < 3
+markupsafe
+jinja2
pyramid-jinja2
pyramid-services
pyramid-mailer
diff --git a/requirements/prod.txt b/requirements/prod.txt
index fa34fbfa523..cb4832354bc 100644
--- a/requirements/prod.txt
+++ b/requirements/prod.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.8
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile requirements/prod.in
@@ -12,10 +12,6 @@ attrs==23.2.0
# via
# jsonschema
# referencing
-backports-zoneinfo[tzdata]==0.2.1
- # via
- # celery
- # kombu
bcrypt==4.1.2
# via -r requirements/prod.in
billiard==4.2.0
@@ -83,28 +79,21 @@ hupper==1.12
# via pyramid
importlib-metadata==7.0.1
# via
- # alembic
- # chameleon
# data-tasks
# h-api
# h-assets
- # markdown
importlib-resources==6.1.1
# via
# -r requirements/prod.in
- # alembic
- # chameleon
# data-tasks
# h-api
- # jsonschema
- # jsonschema-specifications
iso8601==2.1.0
# via
# colander
# deform
itsdangerous==2.1.2
# via -r requirements/prod.in
-jinja2==2.11.3
+jinja2==3.1.3
# via
# -r requirements/prod.in
# data-tasks
@@ -123,7 +112,7 @@ mako==1.3.0
# via alembic
markdown==3.5.2
# via -r requirements/prod.in
-markupsafe==1.1.1
+markupsafe==2.1.3
# via
# -r requirements/prod.in
# jinja2
@@ -144,8 +133,6 @@ pastedeploy==3.1.0
# via plaster-pastedeploy
peppercorn==0.6
# via deform
-pkgutil-resolve-name==1.3.10
- # via jsonschema
plaster==1.1.2
# via
# plaster-pastedeploy
@@ -264,13 +251,9 @@ translationstring==1.4
# deform
# pyramid
typing-extensions==4.9.0
- # via
- # alembic
- # kombu
+ # via alembic
tzdata==2023.4
- # via
- # backports-zoneinfo
- # celery
+ # via celery
urllib3==2.1.0
# via
# elasticsearch
@@ -297,9 +280,7 @@ ws4py==0.5.1
wsaccel==0.6.6
# via -r requirements/prod.in
zipp==3.17.0
- # via
- # importlib-metadata
- # importlib-resources
+ # via importlib-metadata
zope-deprecation==5.0
# via
# deform
diff --git a/requirements/tests.txt b/requirements/tests.txt
index 38f81fe8a6e..d91646f1ece 100644
--- a/requirements/tests.txt
+++ b/requirements/tests.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.8
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile requirements/tests.in
@@ -16,12 +16,6 @@ attrs==23.2.0
# hypothesis
# jsonschema
# referencing
-backports-zoneinfo[tzdata]==0.2.1
- # via
- # -r requirements/prod.txt
- # backports-zoneinfo
- # celery
- # kombu
bcrypt==4.1.2
# via -r requirements/prod.txt
billiard==4.2.0
@@ -92,10 +86,6 @@ elasticsearch==6.8.2
# elasticsearch-dsl
elasticsearch-dsl==6.4.0
# via -r requirements/prod.txt
-exceptiongroup==1.2.0
- # via
- # hypothesis
- # pytest
execnet==2.0.2
# via pytest-xdist
factory-boy==3.3.0
@@ -130,23 +120,15 @@ hypothesis==6.92.9
importlib-metadata==7.0.1
# via
# -r requirements/prod.txt
- # alembic
- # build
- # chameleon
# data-tasks
# h-api
# h-assets
- # markdown
# pip-sync-faster
importlib-resources==6.1.1
# via
# -r requirements/prod.txt
- # alembic
- # chameleon
# data-tasks
# h-api
- # jsonschema
- # jsonschema-specifications
iniconfig==2.0.0
# via pytest
iso8601==2.1.0
@@ -156,7 +138,7 @@ iso8601==2.1.0
# deform
itsdangerous==2.1.2
# via -r requirements/prod.txt
-jinja2==2.11.3
+jinja2==3.1.3
# via
# -r requirements/prod.txt
# data-tasks
@@ -179,7 +161,7 @@ mako==1.3.0
# alembic
markdown==3.5.2
# via -r requirements/prod.txt
-markupsafe==1.1.1
+markupsafe==2.1.3
# via
# -r requirements/prod.txt
# jinja2
@@ -212,10 +194,6 @@ pip-tools==7.3.0
# via
# -r requirements/tests.in
# pip-sync-faster
-pkgutil-resolve-name==1.3.10
- # via
- # -r requirements/prod.txt
- # jsonschema
plaster==1.1.2
# via
# -r requirements/prod.txt
@@ -361,13 +339,6 @@ text-unidecode==1.3
# via
# -r requirements/prod.txt
# python-slugify
-tomli==2.0.1
- # via
- # build
- # coverage
- # pip-tools
- # pyproject-hooks
- # pytest
transaction==4.0
# via
# -r requirements/prod.txt
@@ -385,12 +356,9 @@ typing-extensions==4.9.0
# via
# -r requirements/prod.txt
# alembic
- # faker
- # kombu
tzdata==2023.4
# via
# -r requirements/prod.txt
- # backports-zoneinfo
# celery
urllib3==2.1.0
# via
@@ -433,7 +401,6 @@ zipp==3.17.0
# via
# -r requirements/prod.txt
# importlib-metadata
- # importlib-resources
zope-deprecation==5.0
# via
# -r requirements/prod.txt
diff --git a/tests/unit/h/models/group_test.py b/tests/unit/h/models/group_test.py
index 3709722a6a6..4f79aba4ceb 100644
--- a/tests/unit/h/models/group_test.py
+++ b/tests/unit/h/models/group_test.py
@@ -156,7 +156,7 @@ def test_type_raises_for_unknown_type_of_group(factories):
def test_you_cannot_set_type(factories):
group = factories.Group()
- with pytest.raises(AttributeError, match="can't set attribute"):
+ with pytest.raises(AttributeError, match="object has no setter"):
group.type = "open"
diff --git a/tests/unit/h/models/subscriptions_test.py b/tests/unit/h/models/subscriptions_test.py
index a6194d8ef45..17b82893a23 100644
--- a/tests/unit/h/models/subscriptions_test.py
+++ b/tests/unit/h/models/subscriptions_test.py
@@ -9,5 +9,5 @@ def test___repr__(self):
assert (
repr(subscription)
- == ""
+ == ""
)
diff --git a/tests/unit/h/presenters/annotation_html_test.py b/tests/unit/h/presenters/annotation_html_test.py
index d3abbe45ae3..e31e7a2ad52 100644
--- a/tests/unit/h/presenters/annotation_html_test.py
+++ b/tests/unit/h/presenters/annotation_html_test.py
@@ -1,7 +1,7 @@
import datetime
import pytest
-from jinja2 import Markup
+from markupsafe import Markup
from h.presenters.annotation_html import AnnotationHTMLPresenter
diff --git a/tests/unit/h/presenters/document_html_test.py b/tests/unit/h/presenters/document_html_test.py
index 38fbafc97ee..d107546155d 100644
--- a/tests/unit/h/presenters/document_html_test.py
+++ b/tests/unit/h/presenters/document_html_test.py
@@ -1,7 +1,7 @@
from unittest import mock
-import jinja2
import pytest
+from markupsafe import Markup, escape
from h.presenters.document_html import DocumentHTMLPresenter
@@ -26,7 +26,7 @@ def test_filename_returns_Markup(self):
document_uris=[mock.Mock(uri="file:///home/seanh/MyFile.pdf")]
)
- assert isinstance(presenter.filename, jinja2.Markup)
+ assert isinstance(presenter.filename, Markup)
def test_filename_with_FILE_uri(self):
presenter = self.presenter(
@@ -65,7 +65,7 @@ def test_href_returns_empty_string_for_document_with_no_web_uri(self):
def test_href_returns_Markup(self):
web_uri = "http://www.example.com/example.html"
- assert isinstance(self.presenter(web_uri=web_uri).href, jinja2.Markup)
+ assert isinstance(self.presenter(web_uri=web_uri).href, Markup)
link_text_fixtures = pytest.mark.usefixtures("title")
@@ -105,12 +105,12 @@ def test_link_text_with_https_title(self, title):
@link_text_fixtures
def test_link_text_returns_Markup_if_title_returns_Markup(self, title):
for title_ in (
- jinja2.Markup("Example Document"),
- jinja2.Markup("http://www.example.com/example.html"),
- jinja2.Markup("https://www.example.com/example.html"),
+ Markup("Example Document"),
+ Markup("http://www.example.com/example.html"),
+ Markup("https://www.example.com/example.html"),
):
title.return_value = title_
- assert isinstance(self.presenter().link_text, jinja2.Markup)
+ assert isinstance(self.presenter().link_text, Markup)
hostname_or_filename_fixtures = pytest.mark.usefixtures("uri", "filename")
@@ -122,9 +122,9 @@ def test_hostname_or_filename_returns_filename_for_files(self, filename):
@hostname_or_filename_fixtures
def test_hostname_or_filename_returns_Markup_if_filename_does(self, filename):
- filename.return_value = jinja2.Markup("MyFile.pdf")
+ filename.return_value = Markup("MyFile.pdf")
- assert isinstance(self.presenter().hostname_or_filename, jinja2.Markup)
+ assert isinstance(self.presenter().hostname_or_filename, Markup)
@hostname_or_filename_fixtures
def test_hostname_or_filename_unquotes_filenames(self, filename):
@@ -142,9 +142,9 @@ def test_hostname_or_filename_returns_hostname_for_non_files(self, uri, filename
@hostname_or_filename_fixtures
def test_hostname_or_filename_returns_Markup_when_uri_does(self, uri, filename):
filename.return_value = ""
- uri.return_value = jinja2.Markup("http://www.example.com/example.html")
+ uri.return_value = Markup("http://www.example.com/example.html")
- assert isinstance(self.presenter().hostname_or_filename, jinja2.Markup)
+ assert isinstance(self.presenter().hostname_or_filename, Markup)
@hostname_or_filename_fixtures
def test_hostname_or_filename_with_empty_string_for_uri(self, uri, filename):
@@ -178,10 +178,10 @@ def test_title_escapes_html_in_document_titles(self):
title = self.presenter(title=spam_link).title
- assert jinja2.escape(spam_link) in title
+ assert escape(spam_link) in title
for char in ["<", ">", '"', "'"]:
assert char not in title
- assert isinstance(title, jinja2.Markup)
+ assert isinstance(title, Markup)
@title_fixtures
def test_title_with_file_uri(self, filename):
@@ -193,9 +193,9 @@ def test_title_with_file_uri(self, filename):
@title_fixtures
def test_title_returns_Markup_when_filename_returns_Markup(self, filename):
- filename.return_value = jinja2.Markup("MyFile.pdf")
+ filename.return_value = Markup("MyFile.pdf")
- assert isinstance(self.presenter(title=None).title, jinja2.Markup)
+ assert isinstance(self.presenter(title=None).title, Markup)
@title_fixtures
def test_title_unquotes_uris(self, uri, filename):
@@ -207,9 +207,9 @@ def test_title_unquotes_uris(self, uri, filename):
@title_fixtures
def test_title_returns_Markup_when_uri_returns_Markup(self, uri, filename):
filename.return_value = "" # This is not a file:// URI.
- uri.return_value = jinja2.Markup("http://example.com/example.html")
+ uri.return_value = Markup("http://example.com/example.html")
- assert isinstance(self.presenter(title=None).title, jinja2.Markup)
+ assert isinstance(self.presenter(title=None).title, Markup)
@title_fixtures
def test_title_when_document_has_None_for_title(self, uri, filename):
diff --git a/tox.ini b/tox.ini
index e427f8070c2..17eb5a55872 100644
--- a/tox.ini
+++ b/tox.ini
@@ -52,6 +52,11 @@ filterwarnings =
# See https://github.com/pytest-dev/pytest-xdist/issues/840.
ignore:^The --rsyncdir command line argument and rsyncdirs config variable are deprecated\.:DeprecationWarning
+ # https://github.com/webpy/webpy/issues/732
+ ignore:^'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning:webob
+
+ # https://foss.heptapod.net/python-libs/passlib/-/issues/148
+ ignore:^'crypt' is deprecated and slated for removal in Python 3.13:DeprecationWarning:passlib
[testenv]
skip_install = true
sitepackages = {env:SITE_PACKAGES:false}