diff --git a/sphinxcontrib/websupport/builder.py b/sphinxcontrib/websupport/builder.py
index 060f301..a00d0ff 100644
--- a/sphinxcontrib/websupport/builder.py
+++ b/sphinxcontrib/websupport/builder.py
@@ -2,6 +2,8 @@
from __future__ import annotations
+import html
+import os
from os import path
import posixpath
import shutil
@@ -54,7 +56,7 @@ def init(self) -> None:
raise RuntimeError('websupport builder must be used with '
'the builtin templates')
# add our custom JS
- self.script_files.append('_static/websupport.js')
+ self.add_js_file('websupport.js')
@property
def versioning_method(self):
@@ -133,6 +135,41 @@ def pathto(otheruri: str, resource: bool = False,
self.add_sidebars(pagename, ctx)
ctx.update(addctx)
+ def css_tag(css) -> str:
+ attrs = []
+ for key, value in css.attributes.items():
+ if value is not None:
+ attrs.append(f'{key}="{html.escape(value, quote=True)}"')
+ uri = pathto(os.fspath(css.filename), resource=True)
+ return f''
+
+ ctx['css_tag'] = css_tag
+
+ def js_tag(js) -> str:
+ if not hasattr(js, 'filename'):
+ # str value (old styled)
+ return f''
+
+ attrs = []
+ body = js.attributes.get('body', '')
+ for key, value in js.attributes.items():
+ if key == 'body':
+ continue
+ if value is not None:
+ attrs.append(f'{key}="{html.escape(value, quote=True)}"')
+
+ if not js.filename:
+ if attrs:
+ return f''
+ return f''
+
+ uri = pathto(os.fspath(js.filename), resource=True)
+ if attrs:
+ return f''
+ return f''
+
+ ctx['js_tag'] = js_tag
+
newtmpl = self.app.emit_firstresult('html-page-context', pagename,
templatename, ctx, event_arg)
if newtmpl:
diff --git a/sphinxcontrib/websupport/storage/sqlalchemy_db.py b/sphinxcontrib/websupport/storage/sqlalchemy_db.py
index 578f451..d877081 100644
--- a/sphinxcontrib/websupport/storage/sqlalchemy_db.py
+++ b/sphinxcontrib/websupport/storage/sqlalchemy_db.py
@@ -9,8 +9,8 @@
from sqlalchemy import Column, Integer, Text, String, Boolean, \
ForeignKey, DateTime
-from sqlalchemy.orm import relation, sessionmaker, aliased
-from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import relationship, sessionmaker, aliased, \
+ declarative_base
Base = declarative_base()
Session = sessionmaker()
@@ -127,10 +127,10 @@ class Comment(Base): # type: ignore
path = Column(String(256), index=True)
node_id = Column(String(32), ForeignKey(db_prefix + 'nodes.id'))
- node = relation(Node, backref="comments")
+ node = relationship(Node, backref="comments")
- votes = relation(CommentVote, backref="comment",
- cascade="all")
+ votes = relationship(CommentVote, backref="comment",
+ cascade="all")
def __init__(self, text, displayed, username, rating, time,
proposal, proposal_diff):
diff --git a/sphinxcontrib/websupport/storage/sqlalchemystorage.py b/sphinxcontrib/websupport/storage/sqlalchemystorage.py
index 4c7c849..fea8570 100644
--- a/sphinxcontrib/websupport/storage/sqlalchemystorage.py
+++ b/sphinxcontrib/websupport/storage/sqlalchemystorage.py
@@ -13,8 +13,8 @@
Comment, CommentVote, Session
from sphinxcontrib.websupport.storage.differ import CombinedHtmlDiff
-if sqlalchemy.__version__[:3] < '0.5': # type: ignore
- raise ImportError('SQLAlchemy version 0.5 or greater is required for this '
+if sqlalchemy.__version__[:3] < '1.4': # type: ignore
+ raise ImportError('SQLAlchemy version 1.4 or greater is required for this '
'storage backend; you have version %s' % sqlalchemy.__version__)
@@ -26,7 +26,7 @@ class SQLAlchemyStorage(StorageBackend):
def __init__(self, uri):
self.engine = sqlalchemy.create_engine(uri)
Base.metadata.bind = self.engine
- Base.metadata.create_all()
+ Base.metadata.create_all(bind=self.engine)
Session.configure(bind=self.engine)
def pre_build(self):
@@ -109,7 +109,7 @@ def get_metadata(self, docname, moderator):
func.count('*').label('comment_count')).group_by(
Comment.node_id).subquery()
nodes = session.query(Node.id, subquery.c.comment_count).outerjoin(
- (subquery, Node.id == subquery.c.node_id)).filter(
+ subquery, Node.id == subquery.c.node_id).filter(
Node.document == docname)
session.close()
session.commit()
diff --git a/tests/test_searchadapters.py b/tests/test_searchadapters.py
index 9a7ab73..bcb8cc5 100644
--- a/tests/test_searchadapters.py
+++ b/tests/test_searchadapters.py
@@ -1,21 +1,13 @@
"""Test the Web Support Package search adapters."""
-import shutil
from io import StringIO
-import pytest
-
from sphinxcontrib.websupport import WebSupport
from test_websupport import skip_if_sqlalchemy_missing
from util import skip_unless_importable
-def teardown_module(tmp_path):
- shutil.rmtree(tmp_path / 'websupport', ignore_errors=True)
-
-
-@pytest.fixture
def search_adapter_helper(rootdir, tmp_path, adapter):
support = WebSupport(
srcdir=rootdir / 'test-searchadapters',
@@ -61,5 +53,5 @@ def test_xapian(rootdir, tmp_path):
@skip_unless_importable('whoosh', 'needs whoosh package installed')
@skip_if_sqlalchemy_missing
-def test_whoosh(rootdir, tmp_path, adapter):
+def test_whoosh(rootdir, tmp_path):
search_adapter_helper(rootdir, tmp_path, 'whoosh')
diff --git a/tests/test_websupport.py b/tests/test_websupport.py
index 2191350..64951a2 100644
--- a/tests/test_websupport.py
+++ b/tests/test_websupport.py
@@ -24,7 +24,7 @@
@pytest.fixture
def support(rootdir, tmp_path, request):
settings = {
- 'srcdir': rootdir / 'test-root',
+ 'srcdir': rootdir / 'test-root' / 'root',
# to use same directory for 'builddir' in each 'support' fixture, using
# 'tempdir' (static) value instead of 'tempdir' fixture value.
# each test expect result of db value at previous test case.
@@ -61,6 +61,7 @@ def test_build(support):
@skip_if_sqlalchemy_missing
@with_support()
def test_get_document(support):
+ support.build()
with pytest.raises(DocumentNotFoundError):
support.get_document('nonexisting')
@@ -72,6 +73,7 @@ def test_get_document(support):
@skip_if_sqlalchemy_missing
@with_support()
def test_comments(support):
+ support.build()
session = Session()
nodes = session.query(Node).all()
first_node = nodes[0]
@@ -127,6 +129,7 @@ def get_comment():
session.close()
return support.get_data(node.id)['comments'][0]
+ test_comments(support)
comment = get_comment()
assert comment['username'] == 'user_one'
# Make sure other normal users can't delete someone elses comments.
@@ -150,6 +153,7 @@ def moderation_callback(comment):
@skip_if_sqlalchemy_missing
@with_support(moderation_callback=moderation_callback)
def test_moderation(support):
+ support.build()
session = Session()
nodes = session.query(Node).all()
node = nodes[7]
@@ -182,6 +186,7 @@ def get_comment():
session.close()
return support.get_data(node.id, moderator=True)['comments'][1]
+ test_comments(support)
comment = get_comment()
support.delete_comment(comment['id'], username='user_two',
moderator=True)
@@ -192,6 +197,7 @@ def get_comment():
@skip_if_sqlalchemy_missing
@with_support()
def test_update_username(support):
+ test_comments(support)
support.update_username('user_two', 'new_user_two')
session = Session()
comments = session.query(Comment).\
@@ -211,6 +217,7 @@ def test_update_username(support):
@skip_if_sqlalchemy_missing
@with_support()
def test_proposals(support):
+ support.build()
session = Session()
node = session.query(Node).first()
@@ -227,6 +234,7 @@ def test_proposals(support):
@skip_if_sqlalchemy_missing
@with_support()
def test_voting(support):
+ test_comments(support)
session = Session()
nodes = session.query(Node).all()
node = nodes[0]