Skip to content

Commit 86a06a2

Browse files
author
Release Manager
committed
gh-40737: Speed up the maxima _commands() list Reimplement the `_commands()` method that queries maxima for a list of valid commands. Gives about a 9x speedup here, and should eliminate one of the CI "slow doctest" warnings. URL: #40737 Reported by: Michael Orlitzky Reviewer(s): Frédéric Chapoton, Michael Orlitzky
2 parents ab8ee47 + 5e808ca commit 86a06a2

File tree

1 file changed

+51
-23
lines changed

1 file changed

+51
-23
lines changed

src/sage/interfaces/maxima_abstract.py

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858

5959
from sage.cpython.string import bytes_to_str
6060

61+
from sage.misc.cachefunc import cached_method
6162
from sage.misc.multireplace import multiple_replace
6263
from sage.structure.richcmp import richcmp, rich_to_bool
6364

@@ -291,36 +292,63 @@ def completions(self, s, verbose=True):
291292
cmd_list = [x for x in cmd_list[1:-1].split(',') if x[0] != '?' and not x.endswith('-impl')]
292293
return [x for x in cmd_list if x.find(s) == 0]
293294

294-
def _commands(self, verbose=True):
295+
@cached_method
296+
def _commands(self):
295297
"""
296298
Return list of all commands defined in Maxima.
297299
298-
INPUT:
300+
OUTPUT:
299301
300-
- ``verbose`` -- boolean (default: ``True``)
302+
A list of strings.
301303
302-
OUTPUT: array of strings
304+
EXAMPLES:
303305
304-
EXAMPLES::
306+
The list changes from time to time (with new versions of
307+
Maxima), so we look only for a few reliable commands::
308+
309+
sage: # long time
310+
sage: cs = maxima_calculus._commands()
311+
sage: "display" in cs
312+
True
313+
sage: "gcd" in cs
314+
True
315+
sage: "verbose" in cs
316+
True
305317
306-
# The output is kind of random
307-
sage: sorted(maxima._commands(verbose=False)) # long time (:issue:`39569`)
308-
[...
309-
'display',
310-
...
311-
'gcd',
312-
...
313-
'verbose',
314-
...]
315318
"""
316-
try:
317-
return self.__commands
318-
except AttributeError:
319-
self.__commands = sum(
320-
[self.completions(chr(65+n), verbose=verbose) +
321-
self.completions(chr(97+n), verbose=verbose)
322-
for n in range(26)], [])
323-
return self.__commands
319+
# Passing the empty string to apropos() gets ALL names.
320+
all_names = self._eval_line('apropos("")',
321+
error_check=False).split(",")
322+
323+
# At the time of writing, searching a string for a specific
324+
# character was much much faster than searching a list/tuple.
325+
a_to_Z = "".join(chr(i+j)
326+
for i in range(ord('A'),ord('Z')+1)
327+
for j in (0, 32)) # 'a' = 'A' + 32
328+
329+
# Whack-a-mole to kill junk entries:
330+
#
331+
# * 'erf_%iargs',
332+
# * 'exp\\-form'
333+
# * 'is\\-boole\\-eval'
334+
# * 'is\\-boole\\-verify'
335+
# * 'maybe\\-boole\\-verify'
336+
# * 'time\\/\\/call'
337+
# * 'unknown\\?'
338+
# * 'SPLITS\\ IN\\ Q'
339+
#
340+
# None of these are documented, and the backslash / question
341+
# mark / percent symbol probably aren't going to do what you
342+
# think they're going to do if you type them in an ipython
343+
# shell. We have to trim spaces too because some names show up
344+
# with random leading spaces: ' tminverse', ' toeplitz', etc.
345+
#
346+
bad_chars = ("\\", "/", "?", "%")
347+
return [c
348+
for n in all_names
349+
if (c := n.strip())
350+
and c[0] in a_to_Z
351+
and not any(bad in c for bad in bad_chars)]
324352

325353
def _tab_completion(self, verbose=True, use_disk_cache=True):
326354
r"""
@@ -357,7 +385,7 @@ def _tab_completion(self, verbose=True, use_disk_cache=True):
357385
print("\nBuilding Maxima command completion list (this takes")
358386
print("a few seconds only the first time you do it).")
359387
print("To force rebuild later, delete %s." % COMMANDS_CACHE)
360-
v = self._commands(verbose=verbose)
388+
v = self._commands()
361389
if verbose:
362390
print("\nDone!")
363391
self.__tab_completion = v

0 commit comments

Comments
 (0)