mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Remove REPL code
Summary: Remove repl.py, REPLTab.js, some wiring code, CSS, and a test in testscript.json. Test Plan: NA Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2923
This commit is contained in:
@@ -36,7 +36,6 @@ import table as table_module
|
||||
from user import User # pylint:disable=wrong-import-order
|
||||
import useractions
|
||||
import column
|
||||
import repl
|
||||
import urllib_patch # noqa imported for side effect # pylint:disable=unused-import
|
||||
|
||||
log = logger.Logger(__name__, logger.INFO)
|
||||
@@ -232,9 +231,6 @@ class Engine(object):
|
||||
# A flag for when a useraction causes a schema change, to verify consistency afterwards.
|
||||
self._schema_updated = False
|
||||
|
||||
# Locals dict for recently executed code in the REPL
|
||||
self._repl = repl.REPLInterpreter()
|
||||
|
||||
# Stores an exception representing the first unevaluated cell met while recomputing the
|
||||
# current cell.
|
||||
self._cell_required_error = None
|
||||
@@ -976,11 +972,6 @@ class Engine(object):
|
||||
for (col_obj, values) in cols}
|
||||
)
|
||||
|
||||
def eval_user_code(self, src):
|
||||
ret = self._repl.runsource(src)
|
||||
self.gencode.usercode.__dict__.update(self._repl.locals)
|
||||
return ret
|
||||
|
||||
def invalidate_records(self, table_id, row_ids=depend.ALL_ROWS, col_ids=None,
|
||||
data_cols_to_recompute=frozenset()):
|
||||
"""
|
||||
@@ -1017,8 +1008,6 @@ class Engine(object):
|
||||
def rebuild_usercode(self):
|
||||
"""
|
||||
Compiles the usercode from the schema, and updates all tables and columns to match.
|
||||
Also, keeps the locals in the repl in sync with the user code, so that the repl has access to
|
||||
usercode and vice-versa.
|
||||
"""
|
||||
self.gencode.make_module(self.schema)
|
||||
|
||||
@@ -1040,7 +1029,6 @@ class Engine(object):
|
||||
for table_id, table in six.iteritems(old_tables):
|
||||
if table_id not in self.tables:
|
||||
self._update_table_model(table, None)
|
||||
self._repl.locals.pop(table_id, None)
|
||||
|
||||
# Update docmodel with references to the updated metadata tables.
|
||||
self.docmodel.update_tables()
|
||||
@@ -1048,11 +1036,6 @@ class Engine(object):
|
||||
# Set flag to rebuild dependencies of trigger columns after any potential renames, etc.
|
||||
self.trigger_columns_changed()
|
||||
|
||||
# The order here is important to make sure that when we update the usercode,
|
||||
# we don't overwrite with outdated usercode entries
|
||||
self._repl.locals.update(self.gencode.usercode.__dict__)
|
||||
self.gencode.usercode.__dict__.update(self._repl.locals)
|
||||
|
||||
# Update the context used for autocompletions.
|
||||
self._autocomplete_context = AutocompleteContext(self.gencode.usercode.__dict__)
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
"""
|
||||
This module implements an interpreter for a REPL. It subclasses Python's
|
||||
code.InteractiveInterpreter class, implementing most of its methods, but with
|
||||
slight changes in order to be convenient for Grist's purposes
|
||||
"""
|
||||
|
||||
import code
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
|
||||
import six
|
||||
|
||||
SUCCESS = 0
|
||||
INCOMPLETE = 1
|
||||
ERROR = 2
|
||||
|
||||
EvalTuple = namedtuple("EvalTuple", ("output", "error", "status"))
|
||||
|
||||
#pylint: disable=exec-used, bare-except
|
||||
class REPLInterpreter(code.InteractiveInterpreter):
|
||||
|
||||
def __init__(self):
|
||||
code.InteractiveInterpreter.__init__(self)
|
||||
self.error_text = ""
|
||||
|
||||
def runsource(self, source, filename="<input>", symbol="single"):
|
||||
"""
|
||||
Compiles and executes source. Returns an EvalTuple with a status
|
||||
INCOMPLETE if the code is incomplete,
|
||||
ERROR if it encountered a compilation or run-time error,
|
||||
SUCCESS otherwise.
|
||||
|
||||
an output, which gives all of the output of the user's program
|
||||
(with stderr piped to stdout, essentially, though mock-file objects are used)
|
||||
|
||||
an error, which reports a syntax error at compilation or a runtime error with a
|
||||
Traceback.
|
||||
"""
|
||||
old_stdout = sys.stdout
|
||||
old_stderr = sys.stderr
|
||||
|
||||
user_output = six.StringIO()
|
||||
|
||||
self.error_text = ""
|
||||
try:
|
||||
code = self.compile(source, filename, symbol)
|
||||
except (OverflowError, SyntaxError, ValueError):
|
||||
self.showsyntaxerror(filename)
|
||||
status = ERROR
|
||||
else:
|
||||
status = INCOMPLETE if code is None else SUCCESS
|
||||
|
||||
if status == SUCCESS:
|
||||
|
||||
try:
|
||||
# We use temproray variables to access stdio/stdout
|
||||
# to make sure the client can't do funky things
|
||||
# like get/set attr and have that hurt us
|
||||
sys.stdout = user_output
|
||||
sys.stderr = user_output
|
||||
exec(code, self.locals)
|
||||
except:
|
||||
# bare except to catch absolutely all things the user can throw
|
||||
self.showtraceback()
|
||||
status = ERROR
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
sys.stderr = old_stderr
|
||||
|
||||
program_output = user_output.getvalue()
|
||||
try:
|
||||
user_output.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
return EvalTuple(program_output, self.error_text, status)
|
||||
|
||||
def write(self, txt):
|
||||
"""
|
||||
Used by showsyntaxerror and showtraceback
|
||||
"""
|
||||
self.error_text += txt
|
||||
|
||||
def runcode(self, code):
|
||||
"""
|
||||
This would normally do the part of runsource after compiling the code, but doesn't quite
|
||||
make sense as its own function for our purposes because it couldn't support an INCOMPLETE
|
||||
return value, etc. We explicitly hide it here to make sure the base class's version isn't
|
||||
called by accident.
|
||||
"""
|
||||
raise NotImplementedError("REPLInterpreter.runcode not implemented, use runsource instead")
|
||||
@@ -2709,167 +2709,6 @@
|
||||
}]
|
||||
]
|
||||
}, {
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
"TEST_CASE" : "eval_code",
|
||||
//------------------------------------------------------------------------
|
||||
"BODY": [
|
||||
["LOAD_SAMPLE", "basic"],
|
||||
|
||||
["APPLY", {
|
||||
"USER_ACTIONS": [
|
||||
// Basic tests
|
||||
["EvalCode", "print('cats')", null],
|
||||
["EvalCode", "2 * 3 - 1 // 7 + 4", null],
|
||||
// Exception
|
||||
["EvalCode", "raise Exception('everything broke')", null],
|
||||
// Incomplete structure
|
||||
["EvalCode", "for x in range(1, 100):", null],
|
||||
// Re-evaluation
|
||||
["EvalCode", "print('cats')", 1],
|
||||
["EvalCode", "print('dogs')", 1],
|
||||
// Function definition
|
||||
["EvalCode", "def foo(x):\n\treturn x * 10\n", null],
|
||||
["EvalCode", "foo(10)", null]
|
||||
],
|
||||
"ACTIONS" : {
|
||||
"stored" : [
|
||||
["AddRecord", "_grist_REPL_Hist", 1,
|
||||
{"code": "print('cats')", "errorText": "", "outputText": "cats\n"}],
|
||||
["AddRecord", "_grist_REPL_Hist", 2,
|
||||
{"code": "2 * 3 - 1 // 7 + 4", "errorText": "", "outputText": "10\n"}],
|
||||
["AddRecord", "_grist_REPL_Hist", 3,
|
||||
{"code": "raise Exception('everything broke')", "errorText": "Traceback (most recent call last):\n File \"<input>\", line 1, in <module>\nException: everything broke\n", "outputText": ""}],
|
||||
["UpdateRecord", "_grist_REPL_Hist", 1,
|
||||
{"code": "print('cats')", "errorText": "", "outputText": "cats\n"}],
|
||||
["UpdateRecord", "_grist_REPL_Hist", 1,
|
||||
{"code": "print('dogs')", "errorText": "", "outputText": "dogs\n"}],
|
||||
["AddRecord", "_grist_REPL_Hist", 4,
|
||||
{"code": "def foo(x):\n\treturn x * 10\n", "errorText": "", "outputText": ""}],
|
||||
["AddRecord", "_grist_REPL_Hist", 5,
|
||||
{"code": "foo(10)", "errorText": "", "outputText": "100\n"}]
|
||||
],
|
||||
"direct": [true, true, true, true, true, true, true],
|
||||
"undo" : [
|
||||
["RemoveRecord", "_grist_REPL_Hist", 1],
|
||||
["RemoveRecord", "_grist_REPL_Hist", 2],
|
||||
["RemoveRecord", "_grist_REPL_Hist", 3],
|
||||
["UpdateRecord", "_grist_REPL_Hist", 1,
|
||||
{"code": "print('cats')", "errorText": "", "outputText": "cats\n"}],
|
||||
["UpdateRecord", "_grist_REPL_Hist", 1,
|
||||
{"code": "print('cats')", "errorText": "", "outputText": "cats\n"}],
|
||||
["RemoveRecord", "_grist_REPL_Hist", 4],
|
||||
["RemoveRecord", "_grist_REPL_Hist", 5]
|
||||
],
|
||||
"retValue" : [true,true,true,false,true,true,true,true]
|
||||
}
|
||||
}],
|
||||
|
||||
["APPLY", {
|
||||
"USER_ACTIONS": [
|
||||
// Access to usercode before and after re-generation
|
||||
["EvalCode", "list(map(str, Students.all.firstName))", null],
|
||||
["UpdateRecord", "Students", 1, {"firstName": "2e6"}],
|
||||
["ModifyColumn", "Students", "firstName", { "type" : "Numeric" }],
|
||||
["EvalCode", "list(Students.all.firstName)", 6],
|
||||
// Make sure that other tables are still usable as well after a schema change.
|
||||
["EvalCode", "len(Schools.all)", 6]
|
||||
],
|
||||
"ACTIONS": {
|
||||
"stored": [
|
||||
["AddRecord", "_grist_REPL_Hist", 6,
|
||||
{"code": "list(map(str, Students.all.firstName))", "errorText": "", "outputText": "['Barack', 'George W', 'Bill', 'George H', 'Ronald', 'Jimmy', 'Gerald']\n"}],
|
||||
|
||||
["UpdateRecord", "Students", 1, {"firstName": "2e6"}],
|
||||
["ModifyColumn", "Students", "firstName", {"type": "Numeric"}],
|
||||
["UpdateRecord", "Students", 1, {"firstName": 2e6}],
|
||||
["UpdateRecord", "_grist_Tables_column", 1, {"type": "Numeric"}],
|
||||
|
||||
["UpdateRecord", "_grist_REPL_Hist", 6, {"code": "list(Students.all.firstName)",
|
||||
"errorText": "",
|
||||
"outputText": "[2000000.0, AltText('George W'), AltText('Bill'), AltText('George H'), AltText('Ronald'), AltText('Jimmy'), AltText('Gerald')]\n"}],
|
||||
["UpdateRecord", "_grist_REPL_Hist", 6,
|
||||
{"code": "len(Schools.all)", "errorText": "", "outputText": "6\n"}],
|
||||
["BulkUpdateRecord", "Students", [1, 2, 3, 4, 5, 6, 8], {
|
||||
"fullName": [["E", "TypeError"], ["E","TypeError"], ["E","TypeError"],
|
||||
["E","TypeError"], ["E","TypeError"], ["E","TypeError"], ["E","TypeError"]]
|
||||
}],
|
||||
["BulkUpdateRecord", "Students", [1, 2, 3, 4, 5, 6, 8], {
|
||||
"fullNameLen": [["E","TypeError"], ["E","TypeError"], ["E","TypeError"],
|
||||
["E","TypeError"], ["E","TypeError"], ["E","TypeError"], ["E","TypeError"]]
|
||||
}]
|
||||
],
|
||||
"direct": [true, true, true, false, true, true, true, false, false],
|
||||
"undo": [
|
||||
["RemoveRecord", "_grist_REPL_Hist", 6],
|
||||
|
||||
["UpdateRecord", "Students", 1, {"firstName": "Barack"}],
|
||||
["UpdateRecord", "Students", 1, {"firstName": "2e6"}],
|
||||
["ModifyColumn", "Students", "firstName", {"type": "Text"}],
|
||||
["UpdateRecord", "_grist_Tables_column", 1, {"type": "Text"}],
|
||||
|
||||
["UpdateRecord", "_grist_REPL_Hist", 6, {"code": "list(map(str, Students.all.firstName))",
|
||||
"errorText": "", "outputText": "['Barack', 'George W', 'Bill', 'George H', 'Ronald', 'Jimmy', 'Gerald']\n"}],
|
||||
["UpdateRecord", "_grist_REPL_Hist", 6, {"code": "list(Students.all.firstName)",
|
||||
"errorText": "",
|
||||
"outputText": "[2000000.0, AltText('George W'), AltText('Bill'), AltText('George H'), AltText('Ronald'), AltText('Jimmy'), AltText('Gerald')]\n"}],
|
||||
["BulkUpdateRecord", "Students", [1, 2, 3, 4, 5, 6, 8],
|
||||
{"fullName": ["Barack Obama", "George W Bush", "Bill Clinton", "George H Bush", "Ronald Reagan", "Jimmy Carter", "Gerald Ford"]}],
|
||||
["BulkUpdateRecord", "Students", [1, 2, 3, 4, 5, 6, 8],
|
||||
{"fullNameLen": [12, 13, 12, 13, 13, 12, 11]}]
|
||||
],
|
||||
"retValue": [true,null,null,true,true]
|
||||
}
|
||||
}],
|
||||
|
||||
["APPLY", {
|
||||
"USER_ACTIONS": [
|
||||
// Syntax Error
|
||||
["EvalCode", "not correct c", null],
|
||||
// Other continuations
|
||||
["EvalCode", "map(filter, ", null],
|
||||
["EvalCode", "[1,2,3,", null],
|
||||
// sys.exit
|
||||
["EvalCode", "import sys", null],
|
||||
["EvalCode", "sys.exit(0)", null],
|
||||
// User reassignment of sys.stdout/sys.stderr
|
||||
["EvalCode", "sys.stdout = None", null],
|
||||
["EvalCode", "setattr(sys.stdout, 'getvalue', lambda : 2)", null],
|
||||
["EvalCode", "def foo():\n global stdout\n exec('stdout = 2')\n", null],
|
||||
["EvalCode", "setattr(sys.stderr, 'close', foo)", null]
|
||||
],
|
||||
"ACTIONS": {
|
||||
"stored": [
|
||||
["AddRecord", "_grist_REPL_Hist", 7,
|
||||
{"code": "not correct c", "errorText": " File \"<input>\", line 1\n not correct c\n ^\nSyntaxError: invalid syntax\n", "outputText": ""}],
|
||||
["AddRecord", "_grist_REPL_Hist", 8,
|
||||
{"code": "import sys", "errorText": "", "outputText": ""}],
|
||||
["AddRecord", "_grist_REPL_Hist", 9,
|
||||
{"code": "sys.exit(0)", "errorText": "Traceback (most recent call last):\n File \"<input>\", line 1, in <module>\nSystemExit: 0\n", "outputText": ""}],
|
||||
["AddRecord", "_grist_REPL_Hist", 10,
|
||||
{"code": "sys.stdout = None", "errorText": "", "outputText": ""}],
|
||||
["AddRecord", "_grist_REPL_Hist", 11,
|
||||
{"code": "setattr(sys.stdout, 'getvalue', lambda : 2)", "errorText": "", "outputText": 2}],
|
||||
["AddRecord", "_grist_REPL_Hist", 12,
|
||||
{"code": "def foo():\n global stdout\n exec('stdout = 2')\n", "errorText": "", "outputText": ""}],
|
||||
["AddRecord", "_grist_REPL_Hist", 13,
|
||||
{"code": "setattr(sys.stderr, 'close', foo)", "errorText": "", "outputText": ""}]
|
||||
],
|
||||
"direct": [true, true, true, true, true, true, true],
|
||||
"undo": [
|
||||
["RemoveRecord", "_grist_REPL_Hist", 7],
|
||||
["RemoveRecord", "_grist_REPL_Hist", 8],
|
||||
["RemoveRecord", "_grist_REPL_Hist", 9],
|
||||
["RemoveRecord", "_grist_REPL_Hist", 10],
|
||||
["RemoveRecord", "_grist_REPL_Hist", 11],
|
||||
["RemoveRecord", "_grist_REPL_Hist", 12],
|
||||
["RemoveRecord", "_grist_REPL_Hist", 13]
|
||||
],
|
||||
"retValue" : [true,false,false,true,true,true,true,true,true ]
|
||||
}
|
||||
}]
|
||||
]
|
||||
}, {
|
||||
//------------------------------------------------------------------------
|
||||
"TEST_CASE" : "reserved_keywords",
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
@@ -17,7 +17,6 @@ import schema
|
||||
from schema import RecalcWhen
|
||||
import summary
|
||||
import import_actions
|
||||
import repl
|
||||
import textbuilder
|
||||
import usertypes
|
||||
import treeview
|
||||
@@ -239,37 +238,6 @@ class UserActions(object):
|
||||
"""
|
||||
pass
|
||||
|
||||
#--------------------------------------
|
||||
# User Actions on usercode
|
||||
#--------------------------------------
|
||||
|
||||
@useraction
|
||||
def EvalCode(self, code, row_id):
|
||||
"""
|
||||
Evaluates code in the REPL.
|
||||
a return value of false indicates that the user's code was incomplete
|
||||
(and in this case, we do not create any docactions)
|
||||
otherwise, we either add a new record to the REPL_hist (row_id=null) or Update an existing
|
||||
record (row_id=num) with the results of evaluating the code.
|
||||
"""
|
||||
evaluation = self._engine.eval_user_code(code)
|
||||
if evaluation.status == repl.INCOMPLETE:
|
||||
return False
|
||||
|
||||
|
||||
hist = self._engine.tables["_grist_REPL_Hist"]
|
||||
record = { "code" : code, "outputText" : evaluation.output, "errorText" : evaluation.error }
|
||||
if row_id is None:
|
||||
# This is a new evaluation, append it to the REPL history
|
||||
action = actions.AddRecord(hist.table_id, hist.next_row_id(), record)
|
||||
else:
|
||||
# This is a re-evaluation, update the old retValue
|
||||
action = actions.UpdateRecord(hist.table_id, row_id, record)
|
||||
|
||||
self._do_doc_action(action)
|
||||
|
||||
return True
|
||||
|
||||
#----------------------------------------
|
||||
# User actions on records.
|
||||
#----------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user