Skip to content

♻️ REFACTOR: package code #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.pyc

build/
dist/
Expand Down
7 changes: 5 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,17 @@ repos:
rev: 3.8.4
hooks:
- id: flake8
additional_dependencies: [flake8-bugbear==21.3.1]
additional_dependencies:
- flake8-bugbear~=21.3.1
- flake8-comprehensions~=3.4.0
- pep8-naming~=0.11.1

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.790
hooks:
- id: mypy
additional_dependencies: ["sphinx>=3,<4"]
exclude: tests/roots/test-codeCellTransforms/conf.py
exclude: tests/roots/.*py

- repo: https://github.com/asottile/setup-cfg-fmt
rev: v1.16.0
Expand Down
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ include README.md
include CHANGELOG.md

include jupyterbook_latex/py.typed
recursive-include jupyterbook_latex/theme *
include jupyterbook_latex/theme/jupyterBook.cls
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# jupyterbook-latex
# jupyterbook-latex [IN DEVELOPMENT]

Supporting LaTeX infrastructure for Jupyter Book

Expand All @@ -21,6 +21,22 @@ sphinx:
- jupyterbook_latex
```

## Extension Details

This extension does not provide an actual Sphinx LaTeX theme,
instead it instantiates a number of transforms (for LaTeX builders only) that manipulate the AST into the required format:

1. Overrides some configuration:

- ``latex_engine`` -> ``xelatex``
- ``latex_theme`` -> ``jupyterBook``
- appends necessary LaTeX commands to the preamble

2. When a latex builder is specified:

- Set's up `sphinx.ext.imgconverter`
- Replace headers in the root document

Issues
------

Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py → docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ["_build"]

html_static_path = ["_static"]
# html_static_path = ["_static"]

# -- Options for HTML output -------------------------------------------------

Expand Down
File renamed without changes.
File renamed without changes.
123 changes: 32 additions & 91 deletions jupyterbook_latex/__init__.py
Original file line number Diff line number Diff line change
@@ -1,93 +1,40 @@
import os
from pathlib import Path
""" """

from docutils import nodes as docnodes
from sphinx import builders
from sphinx.util import logging
from sphinx.util.fileutil import copy_asset_file

from .nodes import (
H2Node,
H3Node,
HiddenCellNode,
depart_H2Node,
depart_H3Node,
visit_H2Node,
visit_H3Node,
visit_HiddenCellNode,
)
from .transforms import (
LatexMasterDocTransforms,
ToctreeTransforms,
codeCellTransforms,
handleSubSections,
)
from typing import TYPE_CHECKING, Any, cast

if TYPE_CHECKING:
from sphinx.application import Sphinx

__version__ = "0.2.0"
"""jupyterbook-latex version"""

logger = logging.getLogger(__name__)


# Helper node
def skip(self, node):
raise docnodes.SkipNode


def build_init_handler(app):
from sphinx.util.console import bold
def setup(app: "Sphinx") -> None:
"""The sphinx entry-point for the extension."""

# only allow latex builder to access rest of the features
if isinstance(app.builder, builders.latex.LaTeXBuilder):
app.add_post_transform(codeCellTransforms)
copy_static_files(app)
TOC_PATH = Path(app.confdir).joinpath("_toc.yml")
if not os.path.exists(TOC_PATH):
logger.info(
"Some features of this exetension will work only with a jupyter-book application" # noqa: E501
)
return
app.config["myst_amsmath_enable"] = True
app.setup_extension("sphinx.ext.imgconverter")
app.add_transform(LatexMasterDocTransforms)
app.add_post_transform(ToctreeTransforms)
app.add_post_transform(handleSubSections)
logger.info(
bold("jupyterbook-latex v%s:") + "(latex_engine='%s')",
__version__,
app.config["latex_engine"],
)
from docutils import nodes as docnodes


def add_necessary_config(app, config):
# only allow latex builder to access rest of the features
config["latex_engine"] = "xelatex"
config["latex_theme"] = "jupyterBook"

# preamble to overwrite things from sphinx latex writer
configPreamble = ""
if "preamble" in config["latex_elements"]:
configPreamble = config["latex_elements"]["preamble"]

config["latex_elements"]["preamble"] = (
configPreamble
+ r"""
\usepackage[Latin,Greek]{ucharclasses}
\usepackage{unicode-math}
% fixing title of the toc
\addto\captionsenglish{\renewcommand{\contentsname}{Contents}}
"""
from .events import override_latex_config, setup_latex_transforms
from .nodes import (
HiddenCellNode,
RootHeader,
depart_RootHeader,
visit_HiddenCellNode,
visit_RootHeader,
)
from .transforms import LatexRootDocTransforms

app.add_config_value("jblatex_captions_to_parts", False, "env")
app.add_config_value("jblatex_load_imgconverter", True, "env")

def copy_static_files(app):
themePath = Path(__file__).parent.joinpath("theme")
clsFile = themePath.joinpath("jupyterBook.cls")
copy_asset_file(str(clsFile), app.outdir)
def skip(self, node: docnodes.Element):
raise docnodes.SkipNode

# add_node has the wrong typing for sphinx<4
add_node = cast(Any, app.add_node)

def setup(app):
app.add_node(
add_node(
HiddenCellNode,
override=True,
html=(visit_HiddenCellNode, None),
Expand All @@ -96,23 +43,17 @@ def setup(app):
text=(visit_HiddenCellNode, None),
man=(visit_HiddenCellNode, None),
)
app.add_node(
H2Node,
add_node(
RootHeader,
override=True,
latex=(visit_H2Node, depart_H2Node),
html=(visit_H2Node, depart_H2Node),
latex=(visit_RootHeader, depart_RootHeader),
html=(visit_RootHeader, depart_RootHeader),
textinfo=(skip, None),
text=(skip, None),
man=(skip, None),
)
app.add_node(
H3Node,
override=True,
latex=(visit_H3Node, depart_H3Node),
html=(visit_H3Node, depart_H3Node),
textinfo=(skip, None),
text=(skip, None),
man=(skip, None),
)
app.connect("config-inited", add_necessary_config)
app.connect("builder-inited", build_init_handler)

app.add_transform(LatexRootDocTransforms)

app.connect("config-inited", override_latex_config)
app.connect("builder-inited", setup_latex_transforms)
82 changes: 82 additions & 0 deletions jupyterbook_latex/events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import sys
from typing import cast

from sphinx.application import Sphinx
from sphinx.builders.latex import LaTeXBuilder
from sphinx.config import Config
from sphinx.util import logging
from sphinx.util.fileutil import copy_asset_file

from . import __version__, theme
from .transforms import LatexRootDocPostTransforms, MystNbPostTransform

if sys.version_info < (3, 9):
import importlib_resources as resources
else:
import importlib.resources as resources

logger = logging.getLogger(__name__)


def override_latex_config(app: Sphinx, config: Config) -> None:
"""This ``config-inited`` event overrides aspects of the sphinx latex config.

- ``latex_engine`` -> ``xelatex``
- ``latex_theme`` -> ``jupyterBook``
- appends necessary LaTeX commands to the preamble

"""
# only allow latex builder to access rest of the features
config["latex_engine"] = "xelatex"
config["latex_theme"] = "jupyterBook"

if app.config["jblatex_captions_to_parts"]:
app.config["latex_toplevel_sectioning"] = "part"

latex_elements = cast(dict, config["latex_elements"])

# preamble to overwrite things from sphinx latex writer
config_preamble = (
latex_elements["preamble"] if "preamble" in config["latex_elements"] else ""
)

latex_elements["preamble"] = (
config_preamble
+ r"""
\usepackage[Latin,Greek]{ucharclasses}
\usepackage{unicode-math}
% fixing title of the toc
\addto\captionsenglish{\renewcommand{\contentsname}{Contents}}
"""
)


def setup_latex_transforms(app: Sphinx) -> None:
"""This ``builder-inited`` event sets up aspects of the extension,
reserved only for when a LaTeX builder is specified.
"""

if not isinstance(app.builder, LaTeXBuilder):
return

# note: bold is a dynamically created function
from sphinx.util.console import bold # type: ignore[attr-defined]

logger.info(
bold("jupyterbook-latex v%s:") + "(latex_engine='%s')",
__version__,
app.config["latex_engine"],
)

# Copy the class theme to the output directory.
# note: importlib.resources is the formal method to access files within packages
with resources.as_file(resources.files(theme).joinpath("jupyterBook.cls")) as path:
copy_asset_file(str(path), app.outdir)

# only load when myst-nb is present
if MystNbPostTransform.check_dependency():
app.add_post_transform(MystNbPostTransform)

if app.config["jblatex_load_imgconverter"]:
app.setup_extension("sphinx.ext.imgconverter")
app.add_post_transform(LatexRootDocPostTransforms)
44 changes: 18 additions & 26 deletions jupyterbook_latex/nodes.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
"""AST nodes to designate notebook components."""
from docutils import nodes

from .utils import sphinxEncode

def sphinx_encode(string: str) -> str:
"""Replace tilde, hyphen and single quotes with their LaTeX commands."""
return (
string.replace("~", "\\textasciitilde{}")
.replace("-", "\\sphinxhyphen{}")
.replace("'", "\\textquotesingle{}")
)

def getIndex(body, text):

def get_index(body, text):
index = 0
indices = [i for i, x in enumerate(body) if x == text]
for i in indices:
Expand All @@ -23,19 +30,14 @@ def visit_HiddenCellNode(self, node):
raise nodes.SkipNode


class H2Node(nodes.Element):
def __init__(self, rawsource="", *children, **attributes):
super().__init__("", **attributes)

class RootHeader(nodes.Element):
def __init__(self, rawsource="", *, level: int = 0, **attributes):
super().__init__(rawsource, level=level, **attributes)

class H3Node(nodes.Element):
def __init__(self, rawsource="", *children, **attributes):
super().__init__("", **attributes)

def visit_RootHeader(self, node):

def visit_H2Node(self, node):
self.h2Text = node.astext()
self.h2Text = sphinxEncode(self.h2Text)
node["header_text"] = sphinx_encode(node.astext())

strong = nodes.strong("")
strong.children = node.children
Expand All @@ -50,19 +52,9 @@ def visit_H2Node(self, node):
node.append(line_block)


def depart_H2Node(self, node):
index = getIndex(self.body, self.h2Text)
if index:
self.body[index] = "\\Large " + self.h2Text
# else throw an error


def visit_H3Node(self, node):
visit_H2Node(self, node)


def depart_H3Node(self, node):
index = getIndex(self.body, self.h2Text)
def depart_RootHeader(self, node):
index = get_index(self.body, node["header_text"])
size = "\\Large " if node["level"] <= 2 else "\\large "
if index:
self.body[index] = "\\large " + self.h2Text
self.body[index] = size + node["header_text"]
# else throw an error
Empty file.
Loading