Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
48 changes: 48 additions & 0 deletions docs/source/_extensions/dollarmath.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This program is public domain
# Author: Paul Kienzle
r"""
Allow $math$ markup in text and docstrings, ignoring \$.

The $math$ markup should be separated from the surrounding text by spaces. To
embed markup within a word, place backslash-space before and after. For
convenience, the final $ can be followed by punctuation (period, comma or
semicolon).
"""

import re

_dollar = re.compile(r"(?:^|(?<=\s|[-(]))[$]([^\n]*?)(?<![\\])[$](?:$|(?=\s|[-.,;:?\\)]))")
_notdollar = re.compile(r"\\[$]")

def replace_dollar(content):
content = _dollar.sub(r":math:`\1`",content)
content = _notdollar.sub("$", content)
return content

def rewrite_rst(app, docname, source):
source[0] = replace_dollar(source[0])

def rewrite_autodoc(app, what, name, obj, options, lines):
lines[:] = [replace_dollar(L) for L in lines]

def setup(app):
app.connect('source-read', rewrite_rst)
app.connect('autodoc-process-docstring', rewrite_autodoc)


def test_dollar():
assert replace_dollar("no dollar")=="no dollar"
assert replace_dollar("$only$")==":math:`only`"
assert replace_dollar("$first$ is good")==":math:`first` is good"
assert replace_dollar("so is $last$")=="so is :math:`last`"
assert replace_dollar("and $mid$ too")=="and :math:`mid` too"
assert replace_dollar("$first$, $mid$, $last$")==":math:`first`, :math:`mid`, :math:`last`"
assert replace_dollar(r"dollar\$ escape")=="dollar$ escape"
assert replace_dollar(r"dollar \$escape\$ too")=="dollar $escape$ too"
assert replace_dollar(r"emb\ $ed$\ ed")==r"emb\ :math:`ed`\ ed"
assert replace_dollar("$first$a")=="$first$a"
assert replace_dollar("a$last$")=="a$last$"
assert replace_dollar("a $mid$dle a")=="a $mid$dle a"

if __name__ == "__main__":
test_dollar()
114 changes: 114 additions & 0 deletions docs/source/_extensions/mathjax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""
sphinx.ext.mathjax
~~~~~~~~~~~~~~~~~~

Allow `MathJax <http://mathjax.org/>`_ to be used to display math in
Sphinx's HTML writer -- requires the MathJax JavaScript library on your
webserver/computer.

:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.

sample lines in conf.py to use this::

mathjax_path = ['https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.js',
'http://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/contrib/auto-render.min.js',
'rendermath.js']
mathjax_css = 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.6.0/katex.min.css'

with rendermath.js containing::

document.addEventListener("DOMContentLoaded", function(event) {
renderMathInElement(document.body);
});

2016-08-09 Jason Sachs
* allow list of URLS for mathjax_path, which permits use of katex.js
"""

import sphinx
from docutils import nodes
from sphinx.errors import ExtensionError
from sphinx.ext.mathbase import setup_math as mathbase_setup
from sphinx.locale import _


def html_visit_math(self, node):
self.body.append(self.starttag(node, 'span', '', CLASS='math'))
self.body.append(self.builder.config.mathjax_inline[0] +
self.encode(node['latex']) +
self.builder.config.mathjax_inline[1] + '</span>')
raise nodes.SkipNode


def html_visit_displaymath(self, node):
self.body.append(self.starttag(node, 'div', CLASS='math'))
if node['nowrap']:
self.body.append(self.encode(node['latex']))
self.body.append('</div>')
raise nodes.SkipNode

# necessary to e.g. set the id property correctly
if node['number']:
self.body.append('<span class="eqno">(%s)' % node['number'])
self.add_permalink_ref(node, _('Permalink to this equation'))
self.body.append('</span>')
self.body.append(self.builder.config.mathjax_display[0])
parts = [prt for prt in node['latex'].split('\n\n') if prt.strip()]
if len(parts) > 1: # Add alignment if there are more than 1 equation
self.body.append(r' \begin{align}\begin{aligned}')
for i, part in enumerate(parts):
part = self.encode(part)
if r'\\' in part:
self.body.append(r'\begin{split}' + part + r'\end{split}')
else:
self.body.append(part)
if i < len(parts) - 1: # append new line if not the last equation
self.body.append(r'\\')
if len(parts) > 1: # Add alignment if there are more than 1 equation
self.body.append(r'\end{aligned}\end{align} ')
self.body.append(self.builder.config.mathjax_display[1])
self.body.append('</div>\n')
raise nodes.SkipNode

try:
basestring
except:
basestring = str

def builder_inited(app):
jaxpath = app.config.mathjax_path
if not jaxpath:
raise ExtensionError('mathjax_path config value must be set for the '
'mathjax extension to work')

# app.config.mathjax_path can be a string or a list of strings
if isinstance(jaxpath, basestring):
app.add_javascript(jaxpath)
else:
for p in jaxpath:
app.add_javascript(p)

if app.config.mathjax_css:
app.add_stylesheet(app.config.mathjax_css)


def setup(app):
try:
mathbase_setup(app, (html_visit_math, None), (html_visit_displaymath, None))
except ExtensionError:
raise ExtensionError('sphinx.ext.mathjax: other math package is already loaded')

# more information for mathjax secure url is here:
# http://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn
app.add_config_value('mathjax_path',
'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?'
'config=TeX-MML-AM_CHTML',
False)
app.add_config_value('mathjax_css', None, 'html')
app.add_config_value('mathjax_use_katex', False, 'html')
app.add_config_value('mathjax_inline', [r'\(', r'\)'], 'html')
app.add_config_value('mathjax_display', [r'\[', r'\]'], 'html')
app.connect('builder-inited', builder_inited)

return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
58 changes: 48 additions & 10 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,70 @@
# -- Project information ----------------------------------------------------
import datetime
import os
import sys

from sphinx.domains.python import PythonDomain

from sasdata import __version__ as sasdata_version

if os.path.exists('rst_prolog'):
with open('rst_prolog') as fid:
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath("_extensions")) # for sphinx extensions
print("-- path --")
print("\n".join(sys.path))

if os.path.exists("rst_prolog"):
with open("rst_prolog") as fid:
rst_prolog = fid.read()

# General information about the project.
year = datetime.datetime.now().year

project = 'SasData'
copyright = f'{year}, The SasView Project'
author = 'SasView'
project = "SasData"
copyright = f"{year}, The SasView Project"
author = "SasView"
release = sasdata_version

# -- General configuration ---------------------------------------------------

extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.mathjax",
"dollarmath",
]

templates_path = ['_templates']
templates_path = ["_templates"]
exclude_patterns = []

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

html_theme = 'default'
html_static_path = ['_static']
html_theme = "default"


# Ignore missing references to sections in SasView documentation
def on_missing_reference(app, env, node, contnode):
if node["reftarget"] in [
"file_converter_tool",
"image_viewer_tool",
"sans_calculator_tool",
]:
return contnode
else:
return None


# Bypass stupid sphinx handling of multiple classes with members named *type*
class PatchedPythonDomain(PythonDomain):
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
if "refspecific" in node:
del node["refspecific"]
return super(PatchedPythonDomain, self).resolve_xref(
env, fromdocname, builder, typ, target, node, contnode
)


def setup(app):
app.connect("missing-reference", on_missing_reference)
app.add_domain(PatchedPythonDomain, override=True)
4 changes: 3 additions & 1 deletion docs/source/dev/dev.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ Developer Documentation
.. toctree::
:maxdepth: 8

SasData <generated/sasdata>
SasData <generated/sasdata>

Modules <generated/modules>
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ requires = [
"h5py",
"lxml",
"numpy",
"scipy",
"matplotlib",
]
build-backend = "hatchling.build"

Expand Down Expand Up @@ -94,6 +96,7 @@ header = "SasData"
[[tool.hatch.build.targets.wheel.hooks.sphinx.tools]]
tool = "build"
format = "html"
warnings = true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Please let me know if any additional hatch-sphinx features are needed for this work!)

source = "source"
out_dir = "../build/docs"
environment = { PYTHONPATH=".." }
Expand Down
38 changes: 19 additions & 19 deletions sasdata/data_util/averaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ def __call__(self, data2d: Data2D) -> Data1D:


class CircularAverage(PolarROI):
"""
Calculate I(|Q|) by circularly averaging 2D data between 2 radial limits.
r"""
Calculate $I(|Q|)$ by circularly averaging 2D data between 2 radial limits.

This class is initialised by specifying lower and upper limits on the
magnitude of Q values to consider during the averaging, though currently
Expand All @@ -290,13 +290,13 @@ def __init__(
nbins: int = 100,
base: float | None = None,
) -> None:
"""
r"""
Set up the lower and upper radial limits as well as the number of bins.

The units are A^-1 for the radial parameters.
:param r_min: Lower limit for |Q| values to use during averaging.
:param r_max: Upper limit for |Q| values to use during averaging.
:param nbins: The number of bins data is sorted into along |Q| the axis
:param r_min: Lower limit for $|Q|$ values to use during averaging.
:param r_max: Upper limit for $|Q|$ values to use during averaging.
:param nbins: The number of bins data is sorted into along $|Q|$ the axis
"""
super().__init__(r_range=r_range, center=center)
self.nbins: int = nbins
Expand Down Expand Up @@ -396,12 +396,12 @@ def __init__(
nbins: int = 100,
base: float | None = None,
) -> None:
"""
r"""
Set up the lower and upper radial limits as well as the number of bins.

The units are A^-1 for the radial parameters.
:param r_min: Lower limit for |Q| values to use during averaging.
:param r_max: Upper limit for |Q| values to use during averaging.
:param r_min: Lower limit for $|Q|$ values to use during averaging.
:param r_max: Upper limit for $|Q|$ values to use during averaging.
:param nbins: The number of bins data is sorted into along Phi the axis
"""
super().__init__(r_range=r_range, center=center)
Expand Down Expand Up @@ -549,7 +549,7 @@ def __call__(self, data2d: Data2D = None) -> Data1D:


class SectorQ(PolarROI):
"""
r"""
Project I(Q, φ) data onto I(Q) within a region defined by Cartesian limits.

The projection is computed by averaging together datapoints with the same
Expand All @@ -558,7 +558,7 @@ class SectorQ(PolarROI):

This class is initialised by specifying lower and upper limits on both the
magnitude of Q and the angle φ. These four parameters specify the primary
Region Of Interest, however there is a secondary ROI with the same |Q|
Region Of Interest, however there is a secondary ROI with the same $|Q|$
values on the opposite side of the origin (φ + π). How this secondary ROI
is treated depends on the value of the `fold` parameter. If fold is set to
True, data on opposite sides of the origin are averaged together and the
Expand All @@ -579,14 +579,14 @@ def __init__(
fold: bool = True,
base: float | None = None,
) -> None:
"""
r"""
Set up the ROI boundaries, the binning of the output 1D data, and fold.

The units are A^-1 for radial parameters, and radians for anglar ones.
:param r_range: Tuple (r_min, r_max) defining limits for |Q| values to use during averaging.
:param r_range: Tuple (r_min, r_max) defining limits for $|Q|$ values to use during averaging.
:param phi_range: Tuple (phi_min, phi_max) defining limits for φ in radians (in the primary ROI).
:Defaults to full circle (0, 2*pi).
:param nbins: The number of bins data is sorted into along the |Q| axis
:param nbins: The number of bins data is sorted into along the $|Q|$ axis
:param fold: Whether the primary and secondary ROIs should be folded
together during averaging.
"""
Expand Down Expand Up @@ -709,14 +709,14 @@ def __init__(
nbins: int = 100,
base: float | None = None,
) -> None:
"""
r"""
Set up the ROI boundaries, and the binning of the output 1D data.

The units are A^-1 for radial parameters, and radians for anglar ones.
:param r_range: Tuple (r_min, r_max) defining limits for |Q| values to use during averaging.
:param r_range: Tuple (r_min, r_max) defining limits for $|Q|$ values to use during averaging.
:param phi_range: Tuple (phi_min, phi_max) defining limits for φ in radians (in the primary ROI).
:Defaults to full circle (0, 2*pi).
:param nbins: The number of bins data is sorted into along the |Q| axis
:param nbins: The number of bins data is sorted into along the $|Q|$ axis
"""
super().__init__(r_range=r_range, phi_range=phi_range, center=center)
self.nbins: int = nbins
Expand Down Expand Up @@ -791,11 +791,11 @@ def __init__(
nbins: int = 100,
base: float | None = None,
) -> None:
"""
r"""
Set up the ROI boundaries, and the binning of the output 1D data.

The units are A^-1 for radial parameters, and radians for anglar ones.
:param r_range: Tuple (r_min, r_max) defining limits for |Q| values to use during averaging.
:param r_range: Tuple (r_min, r_max) defining limits for $|Q|$ values to use during averaging.
:param phi_range: Tuple (phi_min, phi_max) defining angular bounds in radians.
Defaults to full circle (0, 2*pi).
:param nbins: The number of bins data is sorted into along the φ axis.
Expand Down
2 changes: 1 addition & 1 deletion sasdata/data_util/binning.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ def get_bin_index(self, value):
2pi to 0 discontinuity.

:param value: A coordinate value in the binning interval along the major axis,
whose bin index should be returned. Must be between min_value and max_value.
whose bin index should be returned. Must be between min_value and max_value.

The general formula logarithm binning is:
bin = floor(N * (log(x) - log(min)) / (log(max) - log(min)))
Expand Down
Loading
Loading