WeBWorK Main Forum

AskSage Problem

AskSage Problem

by F. Heiderich -
Number of replies: 8
Recently AskSage stopped working on my installation. I obtain the following error:

  • ERROR in contacting sage server. Did you accept the terms of service by
  • setting {accepted_tos=>'true'} in the askSage options?
  • IO.pm: ERROR trapped during JSON call to sage:
  • Unable to make a sage call to https://sagecell.sagemath.org/service. at [PG]/lib/WeBWorK/PG/IO.pm line 344
  • Died within WeBWorK::PG::IO::AskSage called at line 770 of [PG]/lib/PGcore.pm
  • from within PGcore::AskSage called at line 149 of [PG]/macros/PG.pl
  • from within main::AskSage called at line 12 of (eval 791)
  • at [PG]/lib/WeBWorK/PG/IO.pm line 399
I am not sure what is the reason, but calling AskSage with debug=>"1" as in the following example problem

DOCUMENT();
loadMacros(
 "PGstandard.pl", # Standard macros for PG language
);
TEXT(beginproblem());
$SageCode=<<END;
print 'This is a string.'
END
$sageReply = AskSage($SageCode,{accepted_tos=>true,debug=>'1',seed=>$problemSeed});
BEGIN_TEXT
$sageReply
END_TEXT
ENDDOCUMENT();

I get the following error:
  • debug is turned on in IO.pm. at [PG]/lib/WeBWorK/PG/IO.pm line 267
  • IO::query_sage_server(): SAGE CALL: /usr/bin/curl -i -k -sS -L --data-urlencode "accepted_tos=true" --data-urlencode 'user_expressions={"WEBWORK":"_webwork_safe_json(WEBWORK)"}' --data-urlencode "code=set_random_seed(1234)
  • WEBWORK={}
  • def _webwork_safe_json(o):
  • import json
  • def default(o):
  • try:
  • if isinstance(o,sage.rings.integer.Integer):
  • json_obj = int(o)
  • elif isinstance(o,(sage.rings.real_mpfr.RealLiteral, sage.rings.real_mpfr.RealNumber)):
  • json_obj = float(o)
  • elif sage.modules.free_module_element.is_FreeModuleElement(o):
  • json_obj = list(o)
  • elif sage.matrix.matrix.is_Matrix(o):
  • json_obj = [list(i) for i in o.rows()]
  • elif isinstance(o, SageObject):
  • json_obj = repr(o)
  • else:
  • raise TypeError
  • except TypeError:
  • pass
  • else:
  • return json_obj
  • # Let the base class default method raise the TypeError
  • return json.JSONEncoder.default(self, o)
  • return json.dumps(o, default=default)
  • print 'This is a string.'" https://sagecell.sagemath.org/service
  • RETURN from sage call
  • HTTP/2 200
  • date: Sat, 23 Sep 2017 13:48:48 GMT
  • content-type: application/json; charset=UTF-8
  • content-length: 221
  • set-cookie: __cfduid=d9605e2e52cb8b328ab8364f7f4725a2b1506174527; expires=Sun, 23-Sep-18 13:48:47 GMT; path=/; domain=.sagemath.org; HttpOnly
  • vary: Accept-Encoding
  • server: cloudflare-nginx
  • cf-ray: 3a2df6ada8d76349-FRA

  • {"execute_reply": {"status": "ok", "execution_count": 1, "user_expressions": {"WEBWORK": {"status": "ok", "data": {"text/plain": "'{}'"}, "metadata": {}}}, "payload": []}, "success": true, "stdout": "This is a string.\n"}
  • END SAGE CALL at [PG]/lib/WeBWorK/PG/IO.pm line 270
  • ERROR in contacting sage server. Did you accept the terms of service by
  • setting {accepted_tos=>'true'} in the askSage options?
  • IO.pm: ERROR trapped during JSON call to sage:
  • Unable to make a sage call to https://sagecell.sagemath.org/service. at [PG]/lib/WeBWorK/PG/IO.pm line 344
  • Died within WeBWorK::PG::IO::AskSage called at line 770 of [PG]/lib/PGcore.pm
  • from within PGcore::AskSage called at line 149 of [PG]/macros/PG.pl
  • from within main::AskSage called at line 12 of (eval 791)
  • at [PG]/lib/WeBWorK/PG/IO.pm line 399

My impression is that the problem occurs in pg/lib/WeBWorK/PG/IO.pm around lines 282-293. Just above these lines there are sample values for $continue, $header and $content. Debugging on my installation I could not reproduce these values. Maybe this due to a change with sagecell.sagemath.org. The comments in IO.pm show "TornadoServer/3.1", while I see "cloudflare-nginx". Maybe something in the HTTP response changed and IO.pm is not parsing it correctly anymore. Can anybody reproduce this problem?
In reply to F. Heiderich

Re: AskSage Problem

by Michael Gage -
I have just come across a similar problem -- but only one of my servers exhibits it. I have not yet tracked the problem to its source. Here are my notes to date:

askSage:
- askSage is working differently on hosted2 than on localhost or on math.webwork.rochester.edu
- on hosted2 the response is HTTP/2 (no OK) and and is not preceeded by an HTTP continue line. On the other machines the other responses are HTTP/1.1 OK and have a continue line preceeding them.
- also the decoding on hosted2. The machine can't handle "success" : true where as the others decode it as a JSON boolean type
 success => type = JSON::PP::Boolean; 1
 }
 
I have not yet been able to track down what is different on the three machines. It does point to a need to make the ability of the askSage subroutine to recognize when a successful connection has been made more robust. Is there
a CPAN module for recognizing HTTP 200 OK in a robust way across many servers? For detecting the end of the header section of a reply? (Currently two
\r\n feeds is used.)

In reply to Michael Gage

Re: AskSage Problem

by F. Heiderich -
I could imagine that the fact that AskSage behaves differently on different hosts might be due to the fact that SageCell uses cloudflare. Maybe the requests from your clients (your webwork servers) end up at different sagecell servers that behave differently.

As for the HTTP problem, there is the following HTTP perl library:

http://search.cpan.org/dist/libwww-perl/lib/LWP.pm

How about using it instead of curl? I never used it myself, but I could imagine that it would help to deal with the HTTP-related problems, since they would hopefully deal better with the different situations that could occur.
In reply to F. Heiderich

Re: AskSage Problem

by Michael Gage -
some progress has been made.

There are two problems. The first is:

  • HTTP/2 200
The askSage and query_sage_server subroutines were set up to handle
HTTP/1.1.

Project: Rewrite query_sage_server to handle both HTTP/2 and HTTP/1.1
return protocols.

Quick hack fix:
Add --http1.1 to the curl command so that (in IO.pm)

my $sagecall = qq{$curlCommand -i -k -sS -L --data-urlencode "accepted_tos=${accepted_tos}"}.
qq{ --data-urlencode 'user_expressions={"WEBWORK":"_webwork_safe_json(WEBWORK)"}' --data-urlencode "code=${setSeed}${webworkfunc}$python" $url};

becomes

my $sagecall = qq{$curlCommand -i -k -sS -L --http1.1 --data-urlencode "accepted_tos=${accepted_tos}"}.
qq{ --data-urlencode 'user_expressions={"WEBWORK":"_webwork_safe_json(WEBWORK)"}' --data-urlencode "code=${setSeed}${webworkfunc}$python" $url};


This forces curl to use HTTP/1.1 protocol whether it wants to or not.
-------------------------------------------

The second issue is

success =>

which arises because in the JSON code we have
"success": true (note the lack of quotes around true which is a python
and JSON reserved term -- but is just a bare word in perl)

Apparently the new versions of the CPAN module JSON don't automatically
fix this:

"
Since version 2.90, stringification (and string comparison) for JSON::true and JSON::false has not been overloaded. It shouldn't matter as long as you treat them as boolean values, but a code that expects they are stringified as "true" or "false" doesn't work as you have expected any more.
if (JSON::true eq 'true') { # now fails

print "The result is 1 now."; # => The result is 1 now.
And now these boolean values don't inherit JSON::Boolean, either. When you need to test a value is a JSON boolean value or not, use JSON::is_bool function, instead of testing the value inherits
"

I'm not sure how to instruct the decode subroutine in JSON to
decode "success": true as if it were "success": "true" or "success": 1.
Nor am I sure yet how to get Sage to return "success" : "true" and if
I did change this would it break other codes?

On one of my machines the success entry is decoded as:

success => type = JSON::PP::Boolean; 1he

which works fine, but that is an older version of JSON by maintained by MAKAMAKA,
where as the newer versions are maintained by ISHIGAKI

The hack for askSage() in IO.pm that I use at the moment is to define $success
as

$success = 1 if $decoded->{execute_reply}->{status} eq 'ok';

and then use $success rather than $decoded->{success} in the rest
of the subroutine.

I need askSage for some current classes so these are the hacks that work at the moment. They are not long term fixes -- they are not robust and don't have many safety checks -- but they at least point out what the issues are and are a start for creating a version of IO.pm that works
for both old versions of HTTP protocol and new as well as old versions
of CPAN's JSON and the new one.






In reply to Michael Gage

Re: AskSage Problem

by Michael Gage -
The hacks that fix the issues discussed in the "askSage Problem" thread have been included in release PG-2.15

They are in commit 89f700467f3fb580c023bc5d28782a3765291a54 submitted in Sept 24 2017.

It would still be worthwhile to review this issue and make it more robust.