Bill Allombert: > On Sun, Dec 11, 2016 at 07:37:00PM +0000, Ximin Luo wrote: >> But the "corrupt" file is not "/usr/share/gap/doc/tut/manual.six.gz", the >> "read" call gave back exactly what was expected. In fact the actual >> "corrupted" files are the manual files from alnuth and autogrp. if you run: >> >> $ sudo gunzip /usr/share/gap/pkg/AutPGrp/doc/manual.six.gz >> $ sudo gunzip /usr/share/gap/pkg/Alnuth/doc/manual.six.gz > > GAP itself handles the compressed old-style .six files correctly. They > are not corrupted. >
OK, thanks for the explanation and sorry for the false alarm. However, I am still quite certain the bug is not caused by the Python pipes issue. I have traced it down to how the gap_reset_workspace() function works in Sage's code, and actually you can reproduce it with only GAP (it segfaults!), without Sage, if you download this file: https://people.debian.org/~infinity0/res/sagetest.py.gap.xz Unzip it, make sure gap-alnuth is installed, then run: | $ echo '?FieldByMatricesNC' | gap -q -L ./sagetest.py.gap | #W corrupted 'manual.six': ##W (in stream: InputTextFile(/usr/share/gap/doc/t\ | ut/manual.six)) | #W corrupted 'manual.six': ##W (in stream: InputTextFile(/usr/share/gap/doc/c\ | hanges/manual.six)) | #W corrupted 'manual.six': intro.tex 1. Introduction | #W (in stream: InputTextFile(/usr/share/gap/pkg/Alnuth/doc/manual.six)) | Error, no method found! For debugging hints type ?Recovery from NoMethodFound | Error, no 1st choice method found for `+' on 2 arguments called from | pos - 1 at /usr/share/gap/lib/helpdef.gi:168 called from | HELP_BOOK_HANDLER.(handler).ReadSix( stream | ) at /usr/share/gap/lib/helpbase.gi:679 called from | [..] | quite a lot of output | [..] | res := SHELL( context, mayReturnVoid, mayReturnObj, 1, false, prompt, false, | "*errin*", "*errout*", false ) | ; at /usr/share/gap/lib/error.g:235 called from | ... at line 1 of *errin* | you can 'return;' after assigning a value | brk_14> Segmentation fault | # exit code 139 It is an issue with gap_reset_workspace() because if you explicitly avoid it, then Sage+GAP can read this file just fine. | $ ./sage -c "print(gap.help('SymmetricGroup', pager=False)[:100])" | [ gives the error ] | | $ ./sage -c "gap=Gap(use_workspace_cache=False); print(gap.help('SymmetricGroup', pager=False)[:100])" | [ doesn't give the error ] To demonstrate this in slightly more detail, I have attached two files: - sagetest.g - this is directly from Sage, for Bill's reference - sagetest.py - this is my attempt to come up with a "minimal test example". I failed, it's not minimal and still requires Sage, however it demonstrates that the problem is to do with Sage's method of "resetting" the gap workspace. Hopefully Jerome can confirm the following, by running in sagemath.git/sage: | sagemath.git/sage$ python sagetest.py 0 # don't use a workspace file | Creation of number fields ______________________ Methods for number fields | | We provide functions to create number fields defined by rational | matrices or by rational polynomials. | | [..] | | Creates a field defined by <polynomial>. The polynomial <polynomial> must | be an irreducible rational polynomial. In the faster NC version, no | checks on the input are performed. | | sagemath.git/sage$ python sagetest.py 1 # i.e. do use a (new) workspace file; it gets "reset" first by Sage | *** Error loading Gap package sonata. You may want to install the gap_packages and/or database_gap SPKGs. | *** Error loading Gap package guava. You may want to install the gap_packages and/or database_gap SPKGs. | *** Error loading Gap package factint. You may want to install the gap_packages and/or database_gap SPKGs. | *** Error loading Gap package grape. You may want to install the gap_packages and/or database_gap SPKGs. | *** Error loading Gap package design. You may want to install the gap_packages and/or database_gap SPKGs. | *** Error loading Gap package toric. You may want to install the gap_packages and/or database_gap SPKGs. | *** Error loading Gap package laguna. You may want to install the gap_packages and/or database_gap SPKGs. | *** Error loading Gap package braid. You may want to install the gap_packages and/or database_gap SPKGs. | Traceback (most recent call last): | File "sagetest.py", line 409, in <module> | print(gap.help('FieldByMatricesNC')) | File "sagetest.py", line 367, in help | line = Expect.eval(self, "? %s"%s) | File "/home/infinity0/var/lib/sage/sagemath/debian/build/usr/lib/python2.7/dist-packages/sage/interfaces/expect.py", line 1299, in eval | for L in code.split('\n') if L != '']) | File "sagetest.py", line 330, in _eval_line | raise RuntimeError(message) | RuntimeError: Gap produced error output | Error, no method found! For debugging hints type ?Recovery from NoMethodFound | Error, no 1st choice method found for `+' on 2 arguments | | executing ? FieldByMatricesNC | # exit code 1 Since you guys know more about GAP than I do, perhaps you can take a look at gap_reset_workspace() in sagetest.py and see if anything looks suspicious? It's still unclear if this is a GAP or a Sage bug. X -- GPG: ed25519/56034877E1F87C35 GPG: rsa4096/1318EFAC5FBBDBCE https://github.com/infinity0/pubkeys.git
# # SAGE support utilities to read into the GAP session. # \$SAGE := rec(); \$SAGE.OldPager := Pager; \$SAGE.NewPager := function( data ) local str, lines, line, fn, start; str := OutputTextFile(\$SAGE.tempfile,false); start := 1; if IsRecord(data) then lines := data.lines; if IsBound(data.start) then start := data.start; fi; else lines := data; fi; if IsString(lines) then lines := SplitString(lines,"\n"); fi; for line in lines do WriteLine(str, line); od; CloseStream(str); Print("Page from ",start,"\n"); end; \$SAGE.StartInteract := function() MakeReadWriteGlobal("Pager"); Pager := \$SAGE.OldPager; HELP_VIEWER_INFO.screen.show := \$SAGE.OldPager; MakeReadOnlyGlobal("Pager"); end; \$SAGE.StopInteract := function() MakeReadWriteGlobal("Pager"); Pager := \$SAGE.NewPager; HELP_VIEWER_INFO.screen.show := \$SAGE.NewPager; MakeReadOnlyGlobal("Pager"); end; \$SAGE.StopInteract(); #\$SAGE.ErrorHandler := function(m,a,m2,mode) # PrintTo("*errout*", m); # if a <> fail then # PrintTo("*errout*",a); # fi; # SetErrorHandler(\$SAGE.ErrorHandler); # return true; #end; #SetErrorHandler(\$SAGE.ErrorHandler); SetAllInfoLevels(0); \$SAGE.OperationsAdmittingFirstArgument := function(obj) local hits, myflags, i, flagss, flags; hits := []; myflags := FlagsType(TypeObj(obj)); for i in [1,3..Length(OPERATIONS)-1] do flagss := OPERATIONS[i+1]; for flags in flagss do if Length(flags) >= 1 and IS_SUBSET_FLAGS(myflags, flags[1]) then Add(hits, OPERATIONS[i]); break; fi; od; od; return hits; end; \$SAGE.CleanOperationName := function(name) local lt, ls; lt := Length("Tester("); if Length(name) > lt and name{[1..lt]} = "Tester(" then return Concatenation("Has",name{[lt+1..Length(name)-1]}); fi; ls := Length("Setter("); if Length(name) > ls and name{[1..ls]} = "Setter(" then return Concatenation("Set",name{[lt+1..Length(name)-1]}); fi; return name; end; \$SAGE.HasAtLeastOneMethodAsFirstArgument := function(op,obj) local t, f, n, meths, i; t := TypeObj(obj); f := FlagsType(t); for n in [1..6] do meths := METHODS_OPERATION(op,n); for i in [1,n+5..LENGTH(meths)-n-3] do if IS_SUBSET_FLAGS(f,meths[i+1]) then return true; fi; od; od; return false; end; \$SAGE.PlausibleTabCompletionsForSage := function(o) local ops, opnames; ops := Filtered(\$SAGE.OperationsAdmittingFirstArgument(o), op -> \$SAGE.HasAtLeastOneMethodAsFirstArgument(op,o)); opnames := List(ops, op -> \$SAGE.CleanOperationName(NameFunction(op))); return Concatenation(opnames, GLOBAL_FUNCTION_NAMES); end; # The log below is for debuging only. # CAREFUL -- do *not* activate this unless you know # what you are doing. E.g., if active and the user doesn't # have write permission to /tmp (e.g., on OS X), # then gap will completely fail to work for them. -- WAS # LogTo("/tmp/gapsage.log"); #
#!/bin/python from sage.interfaces.expect import Expect from sage.interfaces.gap import Gap_generic import re import os import pexpect import sys WORKSPACE = './sagetest.py.gap' gap_cmd = "gap -r" class Gap(Gap_generic): r""" Interface to the GAP interpreter. AUTHORS: - William Stein and David Joyner """ def __init__(self, use_workspace_cache=True, logfile=None): Expect.__init__(self, name='gap', prompt='gap> ', command="%s -b -p -T ./sagetest.g" % ("%s -L %s" % (gap_cmd, WORKSPACE) if use_workspace_cache else gap_cmd), logfile=logfile, eval_using_file_cutoff=100) def _start(self): Expect._start(self, "Failed to start GAP.") # Now, as self._expect exists, we can compile some useful pattern: self._compiled_full_pattern = self._expect.compile_pattern_list([ '@p\d+\.','@@','@[A-Z]','@[123456!"#$%&][^+]*\+', '@e','@c','@f','@h','@i','@m','@n','@r','@s\d','@w.*\+','@x','@z']) # read everything up to the first "ready" prompt self._expect.expect("@i") def help(self, s): tmp_to_use = self._local_tmpfile() self.eval('SetGAPDocTextTheme("none")') self.eval(r'\$SAGE.tempfile := "%s";'%tmp_to_use) line = Expect.eval(self, "? %s"%s) Expect.eval(self, "? 1") match = re.search("Page from (\d+)", line) if match is None: print(line) else: (sline,) = match.groups() F = open(self._local_tmpfile(),"r") help = F.read() return help def save_workspace(self): with open(WORKSPACE, 'wb') as f: f.close() self.eval('SaveWorkspace("%s");'%(f.name), allow_use_file=False) def gap_reset_workspace(): # Create new workspace with filename WORKSPACE g = Gap(use_workspace_cache=False) g.eval('SetUserPreference("HistoryMaxLines", 30)') for pkg in ['GAPDoc', 'ctbllib', 'sonata', 'guava', 'factint', \ 'gapdoc', 'grape', 'design', \ 'toric', 'laguna', 'braid']: # NOTE: Do *not* autoload hap - it screws up PolynomialRing(Rationals,2) try: g.load_package(pkg, verbose=False) except RuntimeError as msg: print('*** %s' % msg) # end for g.save_workspace() g.quit() if os.path.exists(WORKSPACE): os.unlink(WORKSPACE) DO_RESET_WORKSPACE = bool(int(sys.argv[1])) if DO_RESET_WORKSPACE: gap_reset_workspace() gap = Gap(use_workspace_cache=True) else: gap = Gap(use_workspace_cache=False) print(gap.help('FieldByMatricesNC'))