From 7387adb6e7f24991100fd354446e756ef5a118df Mon Sep 17 00:00:00 2001 From: George Stagg Date: Sun, 28 Jul 2024 11:00:42 +0100 Subject: [PATCH] Attempt JS object RDataFrame conversion before RList --- _extensions/live/resources/live-runtime.js | 2 +- live-runtime/src/environment.ts | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/_extensions/live/resources/live-runtime.js b/_extensions/live/resources/live-runtime.js index d4afacf..43ee635 100644 --- a/_extensions/live/resources/live-runtime.js +++ b/_extensions/live/resources/live-runtime.js @@ -79,7 +79,7 @@ var OO=Object.defineProperty;var ci=(i=>typeof require<"u"?require:typeof Proxy< `);Rn||(Rn=document.createElement("script"),Rn.type="application/vnd.jupyter.widget-state+json",Rn=document.body.appendChild(Rn),window.require.config(Yk)),Rn.innerHTML=c;let f=await this.pyodide.toPy({widget:h}),u=await this.pyodide.runPythonAsync(` import json json.dumps(widget) - `,{locals:f});f.destroy();let d=document.createElement("script");d.type="application/vnd.jupyter.widget-view+json",d.innerHTML=u,n.appendChild(d),window.require(["@jupyter-widgets/html-manager/dist/libembed-amd"],function(m){m.renderWidgets()})},a=async h=>{if(t.output){let c=document.createElement("div");c.className="cell-output cell-output-pyodide",c.innerHTML=h,n.appendChild(c)}},l=async(h,c)=>{if(t.output){let f=document.createElement("div"),u=document.createElement("img");f.className="cell-output-display cell-output-pyodide",u.src=`data:${h};base64, ${c}`,f.appendChild(u),n.appendChild(f)}};if(t.echo){let h=document.createElement("div"),c=document.createElement("pre");h.className="sourceCode",c.className="sourceCode python";let f=Pn(this.context.code);c.appendChild(f),h.appendChild(c),n.appendChild(h)}if(e.stdout){let h=document.createElement("div");h.className="exercise-cell-output cell-output cell-output-pyodide cell-output-stdout",h.innerHTML=`
${e.stdout}
`,n.appendChild(h)}if(e.stderr){let h=document.createElement("div");h.className="exercise-cell-output cell-output cell-output-pyodide cell-output-stderr",h.innerHTML=`
${e.stderr}
`,n.appendChild(h)}for(let h=0;hnew t.Shelter),this.env.global=e.then(t=>t.objs.globalEnv)}async toR(e){if(ee(e))return e;let t=await this.shelter;if(e&&e.constructor===Object)e=await new t.RList(e);else if(e&&e.constructor===Array)try{e=await new t.RObject(e)}catch(n){let r=n;if(!r.message.includes("Can't construct `data.frame`"))throw r;e=await Promise.all(e.map(s=>new t.RList(s).then(o=>o)))}return e}async get(e="global"){let t=await this.shelter;return e in this.env||(this.env[e]=t.evalR("new.env(parent = globalenv())")),await this.env[e]}async bind(e,t,n="global"){let r=await this.get(n);t=await this.toR(t),await r.bind(e,t)}async create(e,t){if(e===t||e==="global")return this.get(e);e in this.env&&await this.destroy(e);let n=await this.shelter,r=await this.get(t);return this.env[e]=n.evalR("new.env(parent = parent)",{env:{parent:r}}),await this.env[e]}async destroy(e){if(e=="global"||!(e in this.env))return;let t=await this.shelter,n=await this.env[e];await t.destroy(n),delete this.env[e]}},fa=class{constructor(e){this.env={};this.pyodidePromise=e,this.env.global=e.then(t=>t.toPy({}))}async get(e="global"){let t=await this.pyodidePromise;return e in this.env||(this.env[e]=t.toPy({})),await this.env[e]}async bind(e,t,n="global"){let r=await this.get(n),s=await this.pyodidePromise,o=await s.toPy({environment:r,key:e,value:t});await s.runPythonAsync("environment[key] = value",{locals:o}),o.destroy()}async create(e,t){if(e===t||e==="global")return this.get(e);e in this.env&&await this.destroy(e);let n=await this.pyodidePromise,r=await this.get(t),s=await n.toPy({parent:r}),o=await n.runPythonAsync("parent.copy()",{locals:s});return s.destroy(),this.env[e]=o,await this.env[e]}async destroy(e){if(e=="global"||!(e in this.env))return;await(await this.env[e]).destroy(),delete this.env[e]}};var En=class{constructor(e){this.evaluator=e,this.envManager=this.evaluator.envManager,this.envLabels=this.evaluator.envLabels,this.context=this.evaluator.context,this.options={envir:this.evaluator.options.envir,eval:!0,echo:!1,warning:!0,error:!1,include:!0,output:!0,timelimit:600}}getCheckingAlgorithm(){let e=this.evaluator.options.exercise,t=document.querySelectorAll(`script[type="exercise-check-${e}-contents"]`);if(t.length>0)return t.length>1&&console.warn(`Multiple \`check\` blocks found for exercise "${e}", using the first.`),JSON.parse(atob(t[0].textContent)).code}};var ua=class extends En{constructor(e){super(e),this.webR=this.evaluator.webR}async gradeExercise(){let e=this.context.code;if(!e)return null;let t=await this.blankCheck(e);if(!Se(t))return await this.feedbackAsHtmlAlert(t);if(t=await this.parseCheck(e),!Se(t))return await this.feedbackAsHtmlAlert(t);let n=this.context.indicator;this.context.indicator||(n=new Ke),n.running();try{if(t=await this.evaluateExercise(),Se(t))return null;let r=await this.evaluator.asHtml(t,this.options),s=await r.value.result,o=await(await s.class()).toArray();if(o.includes("gradethis_graded")||o.includes("gradethis_feedback"))return await this.feedbackAsHtmlAlert(s);if(ot(s)){let a=await s.get("message"),l=await s.get("correct");if(!Se(a)&&!Se(l))return await this.feedbackAsHtmlAlert(s)}return r}finally{n.finished(),this.context.indicator||n.destroy()}}async parseCheck(e,t){let n=await this.evaluator.shelter;try{return await n.evalR("parse(text = user_code)",{env:{user_code:e}}),this.evaluator.webR.objs.null}catch{return await new n.RList({message:await n.evalR(`htmltools::HTML(" + `,{locals:f});f.destroy();let d=document.createElement("script");d.type="application/vnd.jupyter.widget-view+json",d.innerHTML=u,n.appendChild(d),window.require(["@jupyter-widgets/html-manager/dist/libembed-amd"],function(m){m.renderWidgets()})},a=async h=>{if(t.output){let c=document.createElement("div");c.className="cell-output cell-output-pyodide",c.innerHTML=h,n.appendChild(c)}},l=async(h,c)=>{if(t.output){let f=document.createElement("div"),u=document.createElement("img");f.className="cell-output-display cell-output-pyodide",u.src=`data:${h};base64, ${c}`,f.appendChild(u),n.appendChild(f)}};if(t.echo){let h=document.createElement("div"),c=document.createElement("pre");h.className="sourceCode",c.className="sourceCode python";let f=Pn(this.context.code);c.appendChild(f),h.appendChild(c),n.appendChild(h)}if(e.stdout){let h=document.createElement("div");h.className="exercise-cell-output cell-output cell-output-pyodide cell-output-stdout",h.innerHTML=`
${e.stdout}
`,n.appendChild(h)}if(e.stderr){let h=document.createElement("div");h.className="exercise-cell-output cell-output cell-output-pyodide cell-output-stderr",h.innerHTML=`
${e.stderr}
`,n.appendChild(h)}for(let h=0;hnew t.Shelter),this.env.global=e.then(t=>t.objs.globalEnv)}async toR(e){if(ee(e))return e;let t=await this.shelter;if(e&&e.constructor===Object)try{e=await new t.RObject(e)}catch(n){let r=n;if(!r.message.includes("Can't construct `data.frame`"))throw r;e=await new t.RList(e)}else if(e&&e.constructor===Array)try{e=await new t.RObject(e)}catch(n){let r=n;if(!r.message.includes("Can't construct `data.frame`"))throw r;e=await Promise.all(e.map(s=>new t.RList(s)))}return e}async get(e="global"){let t=await this.shelter;return e in this.env||(this.env[e]=t.evalR("new.env(parent = globalenv())")),await this.env[e]}async bind(e,t,n="global"){let r=await this.get(n);t=await this.toR(t),await r.bind(e,t)}async create(e,t){if(e===t||e==="global")return this.get(e);e in this.env&&await this.destroy(e);let n=await this.shelter,r=await this.get(t);return this.env[e]=n.evalR("new.env(parent = parent)",{env:{parent:r}}),await this.env[e]}async destroy(e){if(e=="global"||!(e in this.env))return;let t=await this.shelter,n=await this.env[e];await t.destroy(n),delete this.env[e]}},fa=class{constructor(e){this.env={};this.pyodidePromise=e,this.env.global=e.then(t=>t.toPy({}))}async get(e="global"){let t=await this.pyodidePromise;return e in this.env||(this.env[e]=t.toPy({})),await this.env[e]}async bind(e,t,n="global"){let r=await this.get(n),s=await this.pyodidePromise,o=await s.toPy({environment:r,key:e,value:t});await s.runPythonAsync("environment[key] = value",{locals:o}),o.destroy()}async create(e,t){if(e===t||e==="global")return this.get(e);e in this.env&&await this.destroy(e);let n=await this.pyodidePromise,r=await this.get(t),s=await n.toPy({parent:r}),o=await n.runPythonAsync("parent.copy()",{locals:s});return s.destroy(),this.env[e]=o,await this.env[e]}async destroy(e){if(e=="global"||!(e in this.env))return;await(await this.env[e]).destroy(),delete this.env[e]}};var En=class{constructor(e){this.evaluator=e,this.envManager=this.evaluator.envManager,this.envLabels=this.evaluator.envLabels,this.context=this.evaluator.context,this.options={envir:this.evaluator.options.envir,eval:!0,echo:!1,warning:!0,error:!1,include:!0,output:!0,timelimit:600}}getCheckingAlgorithm(){let e=this.evaluator.options.exercise,t=document.querySelectorAll(`script[type="exercise-check-${e}-contents"]`);if(t.length>0)return t.length>1&&console.warn(`Multiple \`check\` blocks found for exercise "${e}", using the first.`),JSON.parse(atob(t[0].textContent)).code}};var ua=class extends En{constructor(e){super(e),this.webR=this.evaluator.webR}async gradeExercise(){let e=this.context.code;if(!e)return null;let t=await this.blankCheck(e);if(!Se(t))return await this.feedbackAsHtmlAlert(t);if(t=await this.parseCheck(e),!Se(t))return await this.feedbackAsHtmlAlert(t);let n=this.context.indicator;this.context.indicator||(n=new Ke),n.running();try{if(t=await this.evaluateExercise(),Se(t))return null;let r=await this.evaluator.asHtml(t,this.options),s=await r.value.result,o=await(await s.class()).toArray();if(o.includes("gradethis_graded")||o.includes("gradethis_feedback"))return await this.feedbackAsHtmlAlert(s);if(ot(s)){let a=await s.get("message"),l=await s.get("correct");if(!Se(a)&&!Se(l))return await this.feedbackAsHtmlAlert(s)}return r}finally{n.finished(),this.context.indicator||n.destroy()}}async parseCheck(e,t){let n=await this.evaluator.shelter;try{return await n.evalR("parse(text = user_code)",{env:{user_code:e}}),this.evaluator.webR.objs.null}catch{return await new n.RList({message:await n.evalR(`htmltools::HTML(" It looks like this might not be valid R code. R cannot determine how to turn your text into a complete command. You may have forgotten to fill in a blank, diff --git a/live-runtime/src/environment.ts b/live-runtime/src/environment.ts index b34e77d..8dec3ae 100644 --- a/live-runtime/src/environment.ts +++ b/live-runtime/src/environment.ts @@ -26,7 +26,15 @@ export class WebREnvironmentManager { const shelter = await this.shelter; if (value && value.constructor === Object) { - value = await new shelter.RList(value); + try { + value = await new shelter.RObject(value); + } catch (_e) { + const e = _e as Error; + if (!e.message.includes("Can't construct `data.frame`")) { + throw e; + } + value = await new shelter.RList(value); + } } else if (value && value.constructor === Array) { try { value = await new shelter.RObject(value); @@ -36,9 +44,7 @@ export class WebREnvironmentManager { throw e; } value = await Promise.all(value.map((v) => { - return new shelter.RList(v).then((obj) => { - return obj; - }) + return new shelter.RList(v); })); } }