Skip to content

Commit

Permalink
Use katex_js_path when pre-rendering (#128)
Browse files Browse the repository at this point in the history
* Use katex_js_path when pre-rendering

* Try to point to non-existing file

* Try to fix command line

* Try to be more verbose

* DEBUG

* Add KATEX_PATH as class method

* Try to point to actual katex.min.js

* DEBUG

* DEBUG

* Fix error

* DEBUG

* Use relative path to katex.min.js

* DEBUG

* Check if ./../ works

* Cleanup debug and comments

* Build documentation without verbose output

* Fix typo

* Update sphinxcontrib/katex-server.js

Co-authored-by: kalvdans <[email protected]>

* Use lower-case class variables

* Improve comment

* Add test and debug

* Improve code and more tests

* Use uppercase for constants

* Fix linter

* Add missing docstring to test

---------

Co-authored-by: kalvdans <[email protected]>
  • Loading branch information
hagenw and kalvdans authored May 16, 2024
1 parent 470ec02 commit 81bf855
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 16 deletions.
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ builtin = 'clear,rare,informal,names'
skip = '*.js,./sphinxcontrib_katex.egg-info,./build'


# ----- pytest ------------------------------------------------------------
#
[tool.pytest.ini_options]
cache_dir = '.cache/pytest'
xfail_strict = true
addopts = '''
--ignore=docs/
'''


# ----- ruff --------------------------------------------------------------
#
[tool.ruff]
Expand Down
8 changes: 5 additions & 3 deletions sphinxcontrib/katex-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ let value = null;
let katex_options = {};
let socket = null;
let port = null;
// this is the downloaded katex javascript library.
// need to keep it updated
// path to the downloaded katex javascript library `katex.min.js`.
// Default path to the KaTeX Javascript library
// without `.js` at the end.
// The default points to the one bundled with the extension.
// Otherwise the file provided with the `katex_js_path`
// config setting is used. The path must start with "./".
let katex_path = "./katex.min";
process.argv.forEach(function(arg) {
if (value == "katex_path") {
Expand Down
47 changes: 34 additions & 13 deletions sphinxcontrib/katex.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@
"throwOnError": False
}

KATEX_PATH = None

# How long to wait for the render server to start in seconds
STARTUP_TIMEOUT = 5.0

Expand Down Expand Up @@ -186,6 +184,8 @@ def builder_inited(app):
# https://github.com/KaTeX/KaTeX/blob/main/contrib/auto-render/README.md
write_katex_autorenderer_file(app, filename_autorenderer)
add_js(filename_autorenderer)
else:
KaTeXServer.katex_path = app.config.katex_js_path
# sphinxcontrib.katex custom CSS
copy_file(app, filename_css)
add_css(filename_css)
Expand Down Expand Up @@ -356,23 +356,26 @@ class KaTeXError(Exception):
class KaTeXServer:
"""Manages and communicates with an instance of the render server."""

# Message length is 32-bit little-endian integer
LENGTH_STRUCT = struct.Struct("<i")
"""Message length for 32-bit little-endian integer."""

katex_path = None
"""Path to KaTeX javascript file."""

# global instance
KATEX_SERVER = None
katex_server = None
"""Global instance of KaTeX server."""

# wait for the server to stop in seconds
STOP_TIMEOUT = 0.1
"""Wait time for the server to stop in seconds."""

@classmethod
def timeout_error(cls, timeout):
"""KaTeX time out error."""
message = STARTUP_TIMEOUT_EXPIRED.format(timeout)
return KaTeXError(message)

@staticmethod
def build_command(socket=None, port=None):
@classmethod
def build_command(cls, socket=None, port=None):
"""KaTeX node build command."""
cmd = [NODEJS_BINARY, SCRIPT_PATH]

Expand All @@ -382,8 +385,26 @@ def build_command(socket=None, port=None):
if port is not None:
cmd.extend(["--port", str(port)])

if KATEX_PATH:
cmd.extend(["--katex", str(KATEX_PATH)])
if cls.katex_path is not None:
# KaTeX will be included inside katex-server.js
# using `require()`,
# which needs the relative path to `katex.min.js`
# without `.js` at the end.
# The default is to use `./katex.min.js`
katex_path = str(cls.katex_path)
if not os.path.isabs(katex_path):
katex_path = os.path.abspath(os.path.join(SRC_DIR, katex_path))
katex_path = os.path.relpath(katex_path, start=SRC_DIR)

# Relative path has to start with `"./"` for `require()`
katex_path = os.path.join("./", katex_path)
if not os.path.exists(os.path.join(SRC_DIR, katex_path)):
raise ValueError(
"KaTeX Javascript library could not be found "
f"at {katex_path}."
)
katex_path = katex_path[:-3] # remove `.js`
cmd.extend(["--katex", katex_path])

return cmd

Expand Down Expand Up @@ -475,10 +496,10 @@ def start(cls):
@classmethod
def get(cls):
"""Get the current render server or start one."""
if cls.KATEX_SERVER is None:
cls.KATEX_SERVER = KaTeXServer.start()
if cls.katex_server is None:
cls.katex_server = KaTeXServer.start()

return cls.KATEX_SERVER
return cls.katex_server

def __init__(self, rundir, process, sock):
self.rundir = rundir
Expand Down
1 change: 1 addition & 0 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pytest
toml
67 changes: 67 additions & 0 deletions tests/test_katex_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import os

import pytest

from sphinxcontrib.katex import KaTeXServer


CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))


@pytest.mark.parametrize(
"katex_js_path, expected_require_path",
[
(
"katex.min.js",
"./katex.min",
),
(
"./katex.min.js",
"./katex.min",
),
(
os.path.join(".", "katex.min.js"),
"./katex.min",
),
(
os.path.join(CURRENT_DIR, "..", "sphinxcontrib", "katex.min.js"),
"./katex.min",
),
],
)
def test_katex_server_js_path(katex_js_path, expected_require_path):
"""Test configured KaTeX Javascript library path.
When using the pre-rendering capabilities
of the ``sphinxcontrib.katex`` sphinx extension,
it requires a KaTeX Javascript library
provided by the file
specified in the config value ``katex_js_path``.
Args:
katex_js_path: path specified for the ``katex_js_path``
config path variable
expected_require_path: expected path
to the KaTeX Javascript library as provided
to the Javascript ``require()`` function.
The path needs to be relative
to the path of ``katex-server.js``
always starts with ``./``,
and exclude the file extension
"""
KaTeXServer.katex_path = katex_js_path
cmd = KaTeXServer.build_command()
assert cmd[-1] == expected_require_path


def test_katex_server_js_path_error():
"""Test errors for configured KaTeX Javascript library path."""
katex_js_path = "non-existing.js"
error_msg = (
"KaTeX Javascript library could not be found at "
f"{os.path.join('.', katex_js_path)}."
)
with pytest.raises(ValueError, match=error_msg):
KaTeXServer.katex_path = katex_js_path
KaTeXServer.build_command()

0 comments on commit 81bf855

Please sign in to comment.