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]