Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 483dce2

Browse files
committed
Correctly end tokens in mathtext parsing.
This avoids parsing `\sinx` as `\sin x` (it now raises an error instead), and removes the need for `accentprefixed` (because `\doteq` is treated as a single token now, instead of `\dot{eq}`). This also means that `\doteq` (and friends) are now correctly treated as relations (per `_relation_symbols`, thus changing the spacing around them); hence then change in baseline images. Only keep the `x \doteq y` baseline (and adjust the test string to undo the spacing), to avoid regen'ing baselines. Also shaves ~2% off drawing all the current mathtext tests, i.e. ``` MPLBACKEND=agg python -c 'import time; from pylab import *; from matplotlib.tests.test_mathtext import math_tests; fig = figure(figsize=(3, 10)); fig.text(0, 0, "\n".join(filter(None, math_tests)), size=6); start = time.perf_counter(); [fig.canvas.draw() for _ in range(10)]; print((time.perf_counter() - start) / 10)' ``` (including adjustment for the two removed test cases), probably because accentprefixed was previously extremely commonly checked, being at the top of the placeable list; however, performance wasn't really the main goal here.
1 parent 60e1ce5 commit 483dce2

File tree

2 files changed

+38
-20
lines changed

2 files changed

+38
-20
lines changed

lib/matplotlib/_mathtext.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,10 +1708,24 @@ def set_names_and_parse_actions():
17081708

17091709
# Root definitions.
17101710

1711+
# In TeX parlance, a csname is a control sequence name (a "\foo").
1712+
def csnames(group, names):
1713+
ends_with_alpha = []
1714+
ends_with_nonalpha = []
1715+
for name in names:
1716+
if name[-1].isalpha():
1717+
ends_with_alpha.append(name)
1718+
else:
1719+
ends_with_nonalpha.append(name)
1720+
return Regex(r"\\(?P<{}>(?:{})(?![A-Za-z]){})".format(
1721+
group,
1722+
"|".join(map(re.escape, ends_with_alpha)),
1723+
"".join(f"|{s}" for s in map(re.escape, ends_with_nonalpha)),
1724+
))
1725+
17111726
p.float_literal = Regex(r"[-+]?([0-9]+\.?[0-9]*|\.[0-9]+)")
17121727
p.space = oneOf(self._space_widths)("space")
17131728

1714-
p.accentprefixed = "\\" + oneOf(self._accentprefixed)("sym")
17151729
p.symbol = Regex(
17161730
r"[a-zA-Z0-9 +\-*/<>=:,.;!\?&'@()\[\]|\U00000080-\U0001ffff]"
17171731
r"|\\[%${}\[\]_|]"
@@ -1720,7 +1734,7 @@ def set_names_and_parse_actions():
17201734
)("sym").leaveWhitespace()
17211735
p.unknown_symbol = Regex(r"\\[A-Za-z]*")("name")
17221736

1723-
p.font = "\\" + oneOf(self._fontnames)("font")
1737+
p.font = csnames("font", self._fontnames)
17241738
p.start_group = (
17251739
Optional(r"\math" + oneOf(self._fontnames)("font")) + "{")
17261740
p.end_group = Literal("}")
@@ -1761,11 +1775,10 @@ def set_names_and_parse_actions():
17611775
| Error(r"Expected \hspace{n}"))
17621776

17631777
p.accent <<= (
1764-
"\\"
1765-
+ oneOf([*self._accent_map, *self._wide_accents])("accent")
1778+
csnames("accent", [*self._accent_map, *self._wide_accents])
17661779
- p.placeable("sym"))
17671780

1768-
p.function <<= "\\" + oneOf(self._function_names)("name")
1781+
p.function <<= csnames("name", self._function_names)
17691782
p.operatorname <<= r"\operatorname" - (
17701783
"{" + ZeroOrMore(p.simple | p.unknown_symbol)("name") + "}"
17711784
| Error(r"Expected \operatorname{name}"))
@@ -1813,10 +1826,8 @@ def set_names_and_parse_actions():
18131826
| Error(r"Expected \underset{annotation}{body}"))
18141827

18151828
p.placeable <<= (
1816-
p.accentprefixed # Must be before accent so named symbols that are
1817-
# prefixed with an accent name work
1818-
| p.accent # Must be before symbol as all accents are symbols
1819-
| p.symbol # Must be third to catch all named symbols and single
1829+
p.accent # Must be before symbol as all accents are symbols
1830+
| p.symbol # Must be second to catch all named symbols and single
18201831
# chars not in a group
18211832
| p.function
18221833
| p.operatorname
@@ -2004,8 +2015,6 @@ def symbol(self, s, loc, toks):
20042015
return [Hlist([char, self._make_space(0.2)], do_kern=True)]
20052016
return [char]
20062017

2007-
accentprefixed = symbol
2008-
20092018
def unknown_symbol(self, s, loc, toks):
20102019
raise ParseFatalException(s, loc, f"Unknown symbol: {toks['name']}")
20112020

@@ -2034,12 +2043,6 @@ def unknown_symbol(self, s, loc, toks):
20342043

20352044
_wide_accents = set(r"widehat widetilde widebar".split())
20362045

2037-
# make a lambda and call it to get the namespace right
2038-
_accentprefixed = (lambda am: [
2039-
p for p in tex2uni
2040-
if any(p.startswith(a) and a != p for a in am)
2041-
])(set(_accent_map))
2042-
20432046
def accent(self, s, loc, toks):
20442047
state = self.get_state()
20452048
thickness = state.get_current_underline_thickness()

lib/matplotlib/tests/test_mathtext.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# If test is removed, use None as placeholder
1717
math_tests = [
1818
r'$a+b+\dot s+\dot{s}+\ldots$',
19-
r'$x \doteq y$',
19+
r'$x\hspace{-0.2}\doteq\hspace{-0.2}y$',
2020
r'\$100.00 $\alpha \_$',
2121
r'$\frac{\$100.00}{y}$',
2222
r'$x y$',
@@ -104,12 +104,12 @@
104104
r'$\mathring{A} \AA$',
105105
r'$M \, M \thinspace M \/ M \> M \: M \; M \ M \enspace M \quad M \qquad M \! M$',
106106
r'$\Cap$ $\Cup$ $\leftharpoonup$ $\barwedge$ $\rightharpoonup$',
107-
r'$\dotplus$ $\doteq$ $\doteqdot$ $\ddots$',
107+
None,
108108
r'$xyz^kx_kx^py^{p-2} d_i^jb_jc_kd x^j_i E^0 E^0_u$', # github issue #4873
109109
r'${xyz}^k{x}_{k}{x}^{p}{y}^{p-2} {d}_{i}^{j}{b}_{j}{c}_{k}{d} {x}^{j}_{i}{E}^{0}{E}^0_u$',
110110
r'${\int}_x^x x\oint_x^x x\int_{X}^{X}x\int_x x \int^x x \int_{x} x\int^{x}{\int}_{x} x{\int}^{x}_{x}x$',
111111
r'testing$^{123}$',
112-
' '.join('$\\' + p + '$' for p in sorted(_mathtext.Parser._accentprefixed)),
112+
None,
113113
r'$6-2$; $-2$; $ -2$; ${-2}$; ${ -2}$; $20^{+3}_{-2}$',
114114
r'$\overline{\omega}^x \frac{1}{2}_0^x$', # github issue #5444
115115
r'$,$ $.$ $1{,}234{, }567{ , }890$ and $1,234,567,890$', # github issue 5799
@@ -223,6 +223,19 @@ def test_mathfont_rendering(baseline_images, fontset, index, text):
223223
horizontalalignment='center', verticalalignment='center')
224224

225225

226+
@check_figures_equal(extensions=["png"])
227+
def test_short_long_accents(fig_test, fig_ref):
228+
acc_map = _mathtext.Parser._accent_map
229+
short_accs = [s for s in acc_map if len(s) == 1]
230+
corresponding_long_accs = []
231+
for s in short_accs:
232+
l, = [l for l in acc_map if len(l) > 1 and acc_map[l] == acc_map[s]]
233+
corresponding_long_accs.append(l)
234+
fig_test.text(0, .5, "$" + "".join(rf"\{s}a" for s in short_accs) + "$")
235+
fig_ref.text(
236+
0, .5, "$" + "".join(fr"\{l} a" for l in corresponding_long_accs) + "$")
237+
238+
226239
def test_fontinfo():
227240
fontpath = mpl.font_manager.findfont("DejaVu Sans")
228241
font = mpl.ft2font.FT2Font(fontpath)
@@ -235,6 +248,7 @@ def test_fontinfo():
235248
[
236249
(r'$\hspace{}$', r'Expected \hspace{n}'),
237250
(r'$\hspace{foo}$', r'Expected \hspace{n}'),
251+
(r'$\sinx$', r'Unknown symbol: \sinx'),
238252
(r'$\frac$', r'Expected \frac{num}{den}'),
239253
(r'$\frac{}{}$', r'Expected \frac{num}{den}'),
240254
(r'$\binom$', r'Expected \binom{num}{den}'),
@@ -265,6 +279,7 @@ def test_fontinfo():
265279
ids=[
266280
'hspace without value',
267281
'hspace with invalid value',
282+
'function without space',
268283
'frac without parameters',
269284
'frac with empty parameters',
270285
'binom without parameters',

0 commit comments

Comments
 (0)