New Upstream Release - sphinx-a4doc
Ready changes
Summary
Merged new upstream version: 1.6.0 (was: 1.3.0).
Resulting package
Built on 2023-08-10T19:28 (took 5m53s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-releases python3-sphinx-a4doc
Lintian Result
Diff
diff --git a/.github/workflows/build-prod.yml b/.github/workflows/build-prod.yml
index b8e60b5..e1dfde8 100644
--- a/.github/workflows/build-prod.yml
+++ b/.github/workflows/build-prod.yml
@@ -48,6 +48,11 @@ jobs:
asset_path: dist/sphinx-a4doc-${{ steps.get_version.outputs.VERSION }}.tar.gz
asset_name: sphinx-a4doc-${{ steps.get_version.outputs.VERSION }}.tar.gz
asset_content_type: application/tar+gzip
+ - name: Publish distribution to Test PyPI
+ uses: pypa/gh-action-pypi-publish@master
+ with:
+ password: ${{ secrets.TEST_PYPI_PASSWORD }}
+ repository_url: https://test.pypi.org/legacy/
- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
diff --git a/README.md b/README.md
index ff984b4..65a4397 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,20 @@ pip3 install sphinx-a4doc
## Changelog
-*v1.2.5*
+*v1.6.0*
+
+- Support LaTeX builder.
+
+*v1.5.0*
+
+- Fixed position of text in diagram nodes in Firefox.
+- Added an option to set custom classes to diagram nodes: `//@ doc:css-class`.
+
+*v1.4.0*
+
+- Fixed compatibility with `singlehtml` mode (see [#15](https://github.com/taminomara/sphinx-a4doc/issues/15)).
+
+*v1.3.0*
- Fixed python 3.9 compatibility issue (by [@sandrotosi](https://github.com/sandrotosi)).
diff --git a/debian/changelog b/debian/changelog
index 6b9e010..6d10e52 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+sphinx-a4doc (1.6.0-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Thu, 10 Aug 2023 19:22:39 -0000
+
sphinx-a4doc (1.3.0-1) unstable; urgency=medium
* New upstream release
diff --git a/debian/patches/dont-use-setuptools-scm.patch b/debian/patches/dont-use-setuptools-scm.patch
index 19ef578..026f493 100644
--- a/debian/patches/dont-use-setuptools-scm.patch
+++ b/debian/patches/dont-use-setuptools-scm.patch
@@ -1,5 +1,7 @@
---- a/setup.py
-+++ b/setup.py
+Index: sphinx-a4doc.git/setup.py
+===================================================================
+--- sphinx-a4doc.git.orig/setup.py
++++ sphinx-a4doc.git/setup.py
@@ -6,7 +6,4 @@ setup(
'Source': 'https://github.com/taminomara/sphinx-a4doc',
'Tracker': 'https://github.com/taminomara/sphinx-a4doc/issues',
@@ -8,11 +10,13 @@
- "local_scheme": "no-local-version"
- }
)
---- a/setup.cfg
-+++ b/setup.cfg
-@@ -30,10 +30,7 @@ install_requires =
- antlr4-python3-runtime==4.7.1
+Index: sphinx-a4doc.git/setup.cfg
+===================================================================
+--- sphinx-a4doc.git.orig/setup.cfg
++++ sphinx-a4doc.git/setup.cfg
+@@ -31,10 +31,7 @@ install_requires =
PyYAML
+ svglib
setup_requires =
- setuptools-scm>=3.5.0
setuptools>=42.0
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 863a4f9..45044ed 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -345,6 +345,11 @@ The list of control comments includes:
- ``//@ doc:name <str>`` -- set a human-readable name for this rule.
See :rst:opt:`a4:rule:name` option.
+- ``//@ doc:css-class`` -- add a custom CSS class to all diagrams
+ referencing this rule.
+
+ .. versionadded:: 1.5.0
+
.. _config:
@@ -360,6 +365,11 @@ To customize diagram style, one can replace
`the default css file <https://github.com/taminomara/sphinx-a4doc/blob/master/sphinx_a4doc/_static/a4_railroad_diagram.css>`_
by placing a ``a4_railroad_diagram.css`` file to the ``_static`` directory.
+.. versionadded:: 1.6.0
+
+ to customise how diagrams look in latex build,
+ place a ``a4_railroad_diagram_latex.css`` file to the ``_static`` directory.
+
.. . .. _custom_lookup:
.. . Customizing process of grammar files lookup
diff --git a/push_docs.py b/push_docs.py
index 11f9793..a349777 100755
--- a/push_docs.py
+++ b/push_docs.py
@@ -44,7 +44,7 @@ def push_html():
os.system('git init')
os.system('git add .')
os.system('git commit -m "update docs"')
- os.system('git push -f ' + REPO + ' master:gh-pages')
+ os.system('git push -f ' + REPO + ' main:gh-pages')
if __name__ == '__main__':
diff --git a/setup.cfg b/setup.cfg
index da68ede..9d3ca7b 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -29,6 +29,7 @@ install_requires =
sphinx>=1.8.0
antlr4-python3-runtime==4.7.1
PyYAML
+ svglib
setup_requires =
setuptools-scm>=3.5.0
setuptools>=42.0
diff --git a/sphinx_a4doc/__init__.py b/sphinx_a4doc/__init__.py
index de0d58a..7138a06 100644
--- a/sphinx_a4doc/__init__.py
+++ b/sphinx_a4doc/__init__.py
@@ -10,7 +10,7 @@ from sphinx_a4doc.autodoc_directive import AutoGrammar, AutoRule
def config_inited(app, config):
static_path = os.path.join(os.path.dirname(__file__), '_static')
- config.html_static_path.insert(0, static_path)
+ config.html_static_path.append(static_path)
def setup(app: sphinx.application.Sphinx):
@@ -20,9 +20,13 @@ def setup(app: sphinx.application.Sphinx):
app.add_node(RailroadDiagramNode,
text=(RailroadDiagramNode.visit_node_text,
- None),
+ RailroadDiagramNode.depart_node),
html=(RailroadDiagramNode.visit_node_html,
- RailroadDiagramNode.depart_node))
+ RailroadDiagramNode.depart_node),
+ latex=(RailroadDiagramNode.visit_node_latex,
+ RailroadDiagramNode.depart_node),
+ man=(RailroadDiagramNode.visit_node_man,
+ RailroadDiagramNode.depart_node))
app.add_directive('railroad-diagram', RailroadDiagram)
app.add_directive('lexer-rule-diagram', LexerRuleDiagram)
diff --git a/sphinx_a4doc/_static/a4_railroad_diagram.css b/sphinx_a4doc/_static/a4_railroad_diagram_latex.css
similarity index 90%
rename from sphinx_a4doc/_static/a4_railroad_diagram.css
rename to sphinx_a4doc/_static/a4_railroad_diagram_latex.css
index c785176..4ad1701 100644
--- a/sphinx_a4doc/_static/a4_railroad_diagram.css
+++ b/sphinx_a4doc/_static/a4_railroad_diagram_latex.css
@@ -8,8 +8,7 @@
font-size: 14px;
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
text-anchor: middle;
- alignment-baseline: central;
- font-weight: bold;
+ dy: 4;
}
.railroad-diagram a {
diff --git a/sphinx_a4doc/autodoc_directive.py b/sphinx_a4doc/autodoc_directive.py
index c70ef06..8c34036 100644
--- a/sphinx_a4doc/autodoc_directive.py
+++ b/sphinx_a4doc/autodoc_directive.py
@@ -352,7 +352,9 @@ class AutoGrammar(Grammar, ModelLoaderMixin, DocsRendererMixin):
):
settings = dataclasses.replace(settings, end_class=EndClass.COMPLEX)
desc_content.append(
- RailroadDiagramNode(dia, settings, grammar)
+ RailroadDiagramNode(
+ '', diagram=dia, options=settings, grammar=grammar
+ )
)
self.render_docs(rule.position.file, docs, desc_content)
@@ -472,7 +474,9 @@ class AutoRule(Rule, ModelLoaderMixin, DocsRendererMixin):
settings = self.diagram_settings
doc_node.append(
- RailroadDiagramNode(dia, settings, grammar)
+ RailroadDiagramNode(
+ '', diagram=dia, options=settings, grammar=grammar
+ )
)
self.render_docs(rule.position.file, docs, doc_node)
diff --git a/sphinx_a4doc/contrib/railroad_diagrams.py b/sphinx_a4doc/contrib/railroad_diagrams.py
index 2442188..0cc6082 100644
--- a/sphinx_a4doc/contrib/railroad_diagrams.py
+++ b/sphinx_a4doc/contrib/railroad_diagrams.py
@@ -67,7 +67,7 @@ def ensure_type(name, x, *types):
if not isinstance(x, types):
types_str = ', '.join([t.__name__ for t in types])
raise ValueError(f'{name} should be {types_str}, '
- f'got {type(x)} instead')
+ f'got {type(x)} ({x!r}) instead')
def ensure_empty_dict(name, x):
@@ -160,11 +160,11 @@ class Diagram:
def node(self, text: str, href: Optional[str] = None, css_class: str = '', radius: int = 0, padding: int = 20, resolve: bool = False, title_is_weak: bool = False) -> 'DiagramItem':
return Node(self, text, href, css_class, radius, padding, resolve, title_is_weak)
- def terminal(self, text: str, href: Optional[str] = None, resolve: bool = True, title_is_weak: bool = False):
- return self.node(text, href, 'node terminal', 10, 20, resolve, title_is_weak)
+ def terminal(self, text: str, href: Optional[str] = None, css_class: str = '', resolve: bool = True, title_is_weak: bool = False):
+ return self.node(text, href, 'node terminal ' + css_class, 10, 20, resolve, title_is_weak)
- def non_terminal(self, text: str, href: Optional[str] = None, resolve: bool = True, title_is_weak: bool = False):
- return self.node(text, href, 'node non-terminal', 0, 20, resolve, title_is_weak)
+ def non_terminal(self, text: str, href: Optional[str] = None, css_class: str = '', resolve: bool = True, title_is_weak: bool = False):
+ return self.node(text, href, 'node non-terminal ' + css_class, 0, 20, resolve, title_is_weak)
def comment(self, text: str, href: Optional[str] = None):
return self.node(text, href, 'node comment', 0, 5)
@@ -300,6 +300,7 @@ class Diagram:
a, kw, self.terminal, (str,), lambda s: ([s], {}),
{
'href': ((str, ), None ),
+ 'css_class': ((str, ), None ),
'resolve': ((bool, ), None ),
'title_is_weak': ((bool, ), None ),
}
@@ -310,6 +311,7 @@ class Diagram:
a, kw, self.non_terminal, (str,), lambda s: ([s], {}),
{
'href': ((str, ), None ),
+ 'css_class': ((str, ), None ),
'resolve': ((bool, ), None ),
'title_is_weak': ((bool, ), None ),
}
@@ -396,12 +398,12 @@ class Diagram:
return [self.load(x)], {}
@overload
- def render(self, root: 'DiagramItem', output: None = None) -> str: ...
+ def render(self, root: 'DiagramItem', output: None = None, style=None) -> str: ...
@overload
- def render(self, root: 'DiagramItem', output: TextIO) -> None: ...
+ def render(self, root: 'DiagramItem', output: TextIO, style=None) -> None: ...
- def render(self, root, output=None):
+ def render(self, root, output=None, style=None):
root = self.sequence(
self.start(),
root,
@@ -423,6 +425,11 @@ class Diagram:
svg.attrs['class'] = 'railroad-diagram'
svg = svg.format()
+ if style:
+ style_r = self.element('style').format()
+ style_r.children.append(style)
+ style_r.add_to(svg)
+
g = self.element('g')
if self.settings.translate_half_pixel:
g.attrs['transform'] = 'translate(.5 .5)'
diff --git a/sphinx_a4doc/diagram_directive.py b/sphinx_a4doc/diagram_directive.py
index 4e12761..08262e1 100644
--- a/sphinx_a4doc/diagram_directive.py
+++ b/sphinx_a4doc/diagram_directive.py
@@ -1,3 +1,6 @@
+import json
+import os.path
+
import docutils.parsers.rst
import docutils.nodes
import docutils.utils
@@ -5,6 +8,9 @@ import sphinx.addnodes
import sphinx.util.docutils
import sphinx.writers.html
import sphinx.writers.text
+import sphinx.writers.latex
+import sphinx.writers.manpage
+import sphinx.util.docutils
import sphinx.util.logging
import sphinx.environment
@@ -54,7 +60,10 @@ class DomainResolver(HrefResolver):
builder = self.builder
env = builder.env
domain = env.get_domain('a4')
- docname = builder.current_docname
+ if hasattr(builder, 'current_docname'):
+ docname = builder.current_docname
+ else:
+ docname = None
xref = sphinx.addnodes.pending_xref(
'',
@@ -92,19 +101,46 @@ class DomainResolver(HrefResolver):
class RailroadDiagramNode(docutils.nodes.Element, docutils.nodes.General):
- def __init__(self, diagram: dict, options: DiagramSettings, grammar: str):
- super().__init__('', diagram=diagram, options=options, grammar=grammar)
+ def __init__(
+ self,
+ rawsource='',
+ *args,
+ diagram: dict,
+ options: DiagramSettings,
+ grammar: str,
+ **kwargs
+ ):
+ super().__init__(
+ rawsource,
+ *args,
+ diagram=diagram,
+ options=options,
+ grammar=grammar,
+ **kwargs
+ )
@staticmethod
- def visit_node_html(self: sphinx.writers.html.HTMLTranslator, node):
+ def node_to_svg(self: sphinx.util.docutils.SphinxTranslator, node, add_style=False):
resolver = DomainResolver(self.builder, node['grammar'])
dia = Diagram(settings=node['options'], href_resolver=resolver)
+ style = None
+ if add_style:
+ for basedir in self.config.html_static_path:
+ path = os.path.join(self.builder.confdir, basedir, 'a4_railroad_diagram_latex.css')
+ if os.path.exists(path):
+ with open(path, 'r') as f:
+ style = f.read()
+ break
try:
data = dia.load(node['diagram'])
- svg = dia.render(data)
+ return dia.render(data, style=style)
except Exception as e:
logger.exception(f'{node.source}:{node.line}: WARNING: {e}')
- else:
+
+ @staticmethod
+ def visit_node_html(self: sphinx.writers.html.HTMLTranslator, node):
+ svg = RailroadDiagramNode.node_to_svg(self, node)
+ if svg:
self.body.append('<p class="railroad-diagram-container">')
self.body.append(svg)
self.body.append('</p>')
@@ -115,9 +151,42 @@ class RailroadDiagramNode(docutils.nodes.Element, docutils.nodes.General):
self.add_text('{}'.format(node['options'].alt))
else:
self.add_text(yaml.dump(node['diagram']))
- raise docutils.nodes.SkipNode
@staticmethod
+ def visit_node_latex(self: sphinx.writers.latex.LaTeXTranslator, node):
+ from svglib.svglib import svg2rlg
+ from reportlab.graphics import renderPDF
+ import io
+ import hashlib
+
+ outdir = os.path.join(self.builder.outdir, 'railroad_diagrams')
+ os.makedirs(outdir, exist_ok=True)
+
+ hash = hashlib.sha256()
+ hash.update(
+ yaml.safe_dump(node['diagram'], sort_keys=True, canonical=True).encode())
+ hash.update(
+ repr(node['options']).encode())
+ pdf_file = f'diagram:{node["grammar"]}:{hash.hexdigest()}.pdf'
+ pdf_file = os.path.join(outdir, pdf_file)
+
+ svg = RailroadDiagramNode.node_to_svg(self, node, add_style=True)
+ svg_file = io.StringIO(svg)
+ rlg = svg2rlg(svg_file)
+
+ renderPDF.drawToFile(rlg, pdf_file)
+
+ self.body.append(
+ f'\n\n\\includegraphics[scale=0.6]{{{pdf_file}}}\n\n'
+ )
+
+ @staticmethod
+ def visit_node_man(self: sphinx.writers.manpage.ManualPageTranslator, node):
+ if node['options'].alt:
+ self.body.append('{}'.format(node['options'].alt))
+ else:
+ self.body.append(yaml.dump(node['diagram']))
+
def depart_node(self, node):
pass
@@ -390,7 +459,11 @@ class RailroadDiagram(sphinx.util.docutils.SphinxDirective, ManagedDirective):
line=self.lineno
)
]
- return [RailroadDiagramNode(content, self.settings, grammar)]
+ return [
+ RailroadDiagramNode(
+ diagram=content, options=self.settings, grammar=grammar
+ )
+ ]
def get_content(self):
return yaml.safe_load('\n'.join(self.content))
diff --git a/sphinx_a4doc/model/impl.py b/sphinx_a4doc/model/impl.py
index 588b58e..884e26c 100644
--- a/sphinx_a4doc/model/impl.py
+++ b/sphinx_a4doc/model/impl.py
@@ -236,6 +236,7 @@ class MetaLoader(ParserVisitor):
is_doxygen_nodoc=True,
is_doxygen_inline=True,
is_doxygen_no_diagram=True,
+ css_class=None,
importance=1,
documentation='',
section=None,
@@ -377,6 +378,7 @@ class LexerRuleLoader(RuleLoader):
is_doxygen_nodoc=doc_info['is_doxygen_nodoc'],
is_doxygen_inline=doc_info['is_doxygen_inline'],
is_doxygen_no_diagram=doc_info['is_doxygen_no_diagram'],
+ css_class=doc_info['css_class'],
importance=doc_info['importance'],
documentation=doc_info['documentation'],
is_fragment=bool(ctx.frag),
@@ -484,6 +486,7 @@ class ParserRuleLoader(RuleLoader):
is_doxygen_nodoc=doc_info['is_doxygen_nodoc'],
is_doxygen_inline=doc_info['is_doxygen_inline'],
is_doxygen_no_diagram=doc_info['is_doxygen_no_diagram'],
+ css_class=doc_info['css_class'],
importance=doc_info['importance'],
documentation=doc_info['documentation'],
section=self._current_section,
@@ -575,6 +578,7 @@ def load_docs(model, tokens, allow_cmd=True):
is_doxygen_nodoc = False
is_doxygen_inline = False
is_doxygen_no_diagram = False
+ css_class = None
importance = 1
name = None
docs: List[Tuple[int, str]] = []
@@ -617,6 +621,11 @@ def load_docs(model, tokens, allow_cmd=True):
if not name:
logger.error(f'{position}: WARNING: name command requires an argument')
continue
+ elif cmd == 'css-class':
+ css_class = match['ctx'].strip()
+ if not name:
+ logger.error(f'{position}: WARNING: css-class command requires an argument')
+ continue
else:
logger.error(f'{position}: WARNING: unknown command {cmd!r}')
@@ -655,6 +664,7 @@ def load_docs(model, tokens, allow_cmd=True):
is_doxygen_inline=is_doxygen_inline,
is_doxygen_nodoc=is_doxygen_nodoc,
is_doxygen_no_diagram=is_doxygen_no_diagram,
+ css_class=css_class,
name=name,
documentation=docs
)
diff --git a/sphinx_a4doc/model/model.py b/sphinx_a4doc/model/model.py
index 90b7c7e..76cfb72 100644
--- a/sphinx_a4doc/model/model.py
+++ b/sphinx_a4doc/model/model.py
@@ -261,6 +261,10 @@ class RuleBase:
If true, generators should not produce railroad diagram for this rule.
"""
+ css_class: Optional[str]
+ """Custom css class set via `//@ doc:css_class`.
+ """
+
is_doxygen_inline: bool
"""Indicates that the `'inline'` flag is set for this rule.
If true, generators should not output any content for this rule.
diff --git a/sphinx_a4doc/model/model_renderer.py b/sphinx_a4doc/model/model_renderer.py
index a15695d..69e2e13 100644
--- a/sphinx_a4doc/model/model_renderer.py
+++ b/sphinx_a4doc/model/model_renderer.py
@@ -97,12 +97,12 @@ class Renderer(CachedRuleContentVisitor[dict]):
return dict(zero_or_more=item, repeat=repeat)
@staticmethod
- def _terminal(text: str, href: Optional[str]=None, resolve: bool = True, title_is_weak: bool = False):
- return dict(terminal=text, href=href, resolve=resolve, title_is_weak=title_is_weak)
+ def _terminal(text: str, href: Optional[str]=None, resolve: bool = True, title_is_weak: bool = False, css_class: Optional[str] = None):
+ return dict(terminal=text, href=href, resolve=resolve, title_is_weak=title_is_weak, css_class=css_class)
@staticmethod
- def _non_terminal(text: str, href: Optional[str]=None, resolve: bool = True, title_is_weak: bool = False):
- return dict(non_terminal=text, href=href, resolve=resolve, title_is_weak=title_is_weak)
+ def _non_terminal(text: str, href: Optional[str]=None, resolve: bool = True, title_is_weak: bool = False, css_class: Optional[str] = None):
+ return dict(non_terminal=text, href=href, resolve=resolve, title_is_weak=title_is_weak, css_class=css_class)
@staticmethod
def _comment(text: str, href: Optional[str]=None):
@@ -163,15 +163,16 @@ class Renderer(CachedRuleContentVisitor[dict]):
literal = str(rule.content)
if self.literal_rendering is LiteralRendering.CONTENTS_UNQUOTED:
literal = literal[1:-1]
- return self._terminal(literal, path)
+ return self._terminal(literal, path, css_class=rule.css_class)
else:
name = rule.display_name or self._cc_to_dash(rule.name)
- return self._terminal(name, path, title_is_weak=True)
+ return self._terminal(name, path, title_is_weak=True, css_class=rule.css_class)
elif isinstance(rule, ParserRule):
return self._non_terminal(
rule.display_name or self._cc_to_dash(rule.name),
f'{rule.model.get_name()}.{rule.name}',
- title_is_weak=True)
+ title_is_weak=True,
+ css_class=rule.css_class)
else:
assert False
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/lib/python3/dist-packages/sphinx_a4doc/_static/a4_railroad_diagram_latex.css
Control files: lines which differ (wdiff format)
Depends: python3-antlr4, python3-sphinx, python3-svglib, python3-yaml, python3:any