jupyter
This commit is contained in:
@@ -64,6 +64,8 @@ yap_ipython/frontend.py
|
||||
yap_ipython/parallel.py
|
||||
yap_ipython/html.py
|
||||
yap_ipython/__main__.py
|
||||
_version.py
|
||||
yap_ipython/_version.py
|
||||
yap_ipython/testing/iptest.py
|
||||
yap_ipython/testing/skipdoctest.py
|
||||
yap_ipython/testing/tools.py
|
||||
@@ -387,7 +389,7 @@ set(FILES ${PYTHON_SOURCES} ${PL_SOURCES} ${EXTRAS} ${RESOURCES})
|
||||
set(SETUP_PY ${CMAKE_CURRENT_BINARY_DIR}/setup.py)
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/yap.tgz
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cf ${CMAKE_CURRENT_BINARY_DIR}/yap.tgz ${FILES}
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cf ${CMAKE_CURRENT_BINARY_DIR}/yap.tgz ${FILES}
|
||||
|
||||
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
@@ -421,13 +423,14 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/yap_kernel/resources/logo-
|
||||
|
||||
add_custom_target(YAP_KERNEL ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E tar xzf yap.tgz
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} build sdist bdist
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/yap_kernel/resources/logo-32x32.png ${CMAKE_CURRENT_BINARY_DIR}/yap_kernel/resources/logo-64x64.png yap.tgz ${CMAKE_CURRENT_BINARY_DIR}/yap_kernel/resources/kernel.js ${CMAKE_CURRENT_BINARY_DIR}/yap_kernel/resources/prolog.js
|
||||
)
|
||||
|
||||
|
||||
install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pip install --ignore-installed --no-deps .
|
||||
install(CODE "execute_process(
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} build sdist bdist
|
||||
COMMAND ${PYTHON_EXECUTABLE} -m pip install --ignore-installed --no-deps .
|
||||
COMMAND ${PYTHON_EXECUTABLE} -m yap_kernel.kernelspec
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
|
||||
|
||||
|
@@ -40,14 +40,15 @@ here = os.path.abspath(os.path.dirname(__file__))
|
||||
packages = ['yap_kernel','yap_ipython']
|
||||
# pkg_root = pjoin(here, name)
|
||||
|
||||
# for d, _, _ in os.walk(pjoin(here, 'yap_kernel')):
|
||||
# if os.path.exists(pjoin(d, '__init__.py')):
|
||||
# packages.append(d[len(here)+1:].replace(os.path.sep, '.'))
|
||||
# for d, _, _ in os.walk(pjoin(here, 'yap_ipython')):
|
||||
# if os.path.exists(pjoin(d, '__init__.py')):
|
||||
# packages.append(d[len(here)+1:].replace(os.path.sep, '.'))
|
||||
for d, _, _ in os.walk(pjoin(here, 'yap_kernel')):
|
||||
if os.path.exists(pjoin(d, '__init__.py')):
|
||||
packages.append(d[len(here)+1:].replace(os.path.sep, '.'))
|
||||
for d, _, _ in os.walk(pjoin(here, 'yap_ipython')):
|
||||
if os.path.exists(pjoin(d, '__init__.py')):
|
||||
packages.append(d[len(here)+1:].replace(os.path.sep, '.'))
|
||||
|
||||
sys.path.insert(0, here)
|
||||
sys.path.insert(0, pjoin(here,'..','swig'))
|
||||
package_data = {
|
||||
'yap_ipython': ['prolog/*.*'],
|
||||
'yap_kernel': ['resources/*.*']
|
||||
|
@@ -32,15 +32,15 @@ This is an handy alias to `ipython history trim --keep=0`
|
||||
|
||||
class HistoryTrim(BaseYAPApplication):
|
||||
description = trim_hist_help
|
||||
|
||||
|
||||
backup = Bool(False,
|
||||
help="Keep the old history file as history.sqlite.<N>"
|
||||
).tag(config=True)
|
||||
|
||||
|
||||
keep = Int(1000,
|
||||
help="Number of recent lines to keep in the database."
|
||||
).tag(config=True)
|
||||
|
||||
|
||||
flags = Dict(dict(
|
||||
backup = ({'HistoryTrim' : {'backup' : True}},
|
||||
backup.help
|
||||
@@ -50,7 +50,7 @@ class HistoryTrim(BaseYAPApplication):
|
||||
aliases=Dict(dict(
|
||||
keep = 'HistoryTrim.keep'
|
||||
))
|
||||
|
||||
|
||||
def start(self):
|
||||
profile_dir = self.profile_dir.location
|
||||
hist_file = os.path.join(profile_dir, 'history.sqlite')
|
||||
@@ -63,9 +63,9 @@ class HistoryTrim(BaseYAPApplication):
|
||||
print("There are already at most %d entries in the history database." % self.keep)
|
||||
print("Not doing anything. Use --keep= argument to keep fewer entries")
|
||||
return
|
||||
|
||||
|
||||
print("Trimming history to the most recent %d entries." % self.keep)
|
||||
|
||||
|
||||
inputs.pop() # Remove the extra element we got to check the length.
|
||||
inputs.reverse()
|
||||
if inputs:
|
||||
@@ -75,7 +75,7 @@ class HistoryTrim(BaseYAPApplication):
|
||||
sessions = list(con.execute('SELECT session, start, end, num_cmds, remark FROM '
|
||||
'sessions WHERE session >= ?', (first_session,)))
|
||||
con.close()
|
||||
|
||||
|
||||
# Create the new history database.
|
||||
new_hist_file = os.path.join(profile_dir, 'history.sqlite.new')
|
||||
i = 0
|
||||
@@ -114,18 +114,18 @@ class HistoryTrim(BaseYAPApplication):
|
||||
print("Backed up longer history file to", backup_hist_file)
|
||||
else:
|
||||
os.remove(hist_file)
|
||||
|
||||
|
||||
os.rename(new_hist_file, hist_file)
|
||||
|
||||
class HistoryClear(HistoryTrim):
|
||||
description = clear_hist_help
|
||||
keep = Int(0,
|
||||
help="Number of recent lines to keep in the database.")
|
||||
|
||||
|
||||
force = Bool(False,
|
||||
help="Don't prompt user for confirmation"
|
||||
).tag(config=True)
|
||||
|
||||
|
||||
flags = Dict(dict(
|
||||
force = ({'HistoryClear' : {'force' : True}},
|
||||
force.help),
|
||||
|
@@ -219,9 +219,9 @@ class ExecutionResult(object):
|
||||
|
||||
def raise_error(self):
|
||||
"""Reraises error if `success` is `False`, otherwise does nothing"""
|
||||
if self.error_before_exec is not None:
|
||||
if self.error_before_exec:
|
||||
raise self.error_before_exec
|
||||
if self.error_in_exec is not None:
|
||||
if self.error_in_exec:
|
||||
raise self.error_in_exec
|
||||
|
||||
def __repr__(self):
|
||||
@@ -2598,7 +2598,7 @@ class InteractiveShell(SingletonConfigurable):
|
||||
with prepended_to_syspath(dname):
|
||||
try:
|
||||
for cell in get_cells():
|
||||
result = self.run_cell(cell, silent=False , shell_futures=shell_futures)
|
||||
result = self.run_cell(cell, silent=False, shell_futures=shell_futures)
|
||||
if raise_exceptions:
|
||||
result.raise_error()
|
||||
elif not result.success:
|
||||
@@ -2661,6 +2661,9 @@ class InteractiveShell(SingletonConfigurable):
|
||||
-------
|
||||
result : :class:`ExecutionResult`
|
||||
"""
|
||||
|
||||
print("go")
|
||||
result = None
|
||||
try:
|
||||
result = self._yrun_cell(
|
||||
raw_cell, store_history, silent, shell_futures)
|
||||
@@ -2668,6 +2671,7 @@ class InteractiveShell(SingletonConfigurable):
|
||||
self.events.trigger('post_execute')
|
||||
if not silent:
|
||||
self.events.trigger('post_run_cell', result)
|
||||
print("go", result)
|
||||
return result
|
||||
|
||||
def _run_cell(self, raw_cell, store_history, silent, shell_futures):
|
||||
|
@@ -11,15 +11,15 @@
|
||||
* -
|
||||
*/
|
||||
|
||||
%% :- module( jupyter,
|
||||
%% [jupyter_query/3,
|
||||
%% errors/2,
|
||||
%% ready/2,
|
||||
%% completion/2,
|
||||
|
||||
%% ]
|
||||
%% ).
|
||||
% :- module( jupyter,
|
||||
% [jupyter_query/3,
|
||||
% errors/2,
|
||||
% ready/2,
|
||||
% completion/2,
|
||||
|
||||
% ]
|
||||
%% ).
|
||||
:- [library(hacks)].
|
||||
|
||||
:- reexport(library(yapi)).
|
||||
:- use_module(library(lists)).
|
||||
@@ -32,26 +32,24 @@ jupyter_query(Caller, Cell, Line ) :-
|
||||
jupyter_cell(Caller, Cell, Line).
|
||||
|
||||
jupyter_cell(_Caller, Cell, _) :-
|
||||
jupyter_consult(Cell),
|
||||
jupyter_consult(Cell), %stack_dump,
|
||||
fail.
|
||||
jupyter_cell( _Caller, _, Line ) :-
|
||||
blank( Line ),
|
||||
!.
|
||||
jupyter_cell( _Caller, _, [] ) :- !.
|
||||
jupyter_cell( Caller, _, Line ) :-
|
||||
gated_call(
|
||||
enter_cell(call),
|
||||
python_query( Caller, Line ),
|
||||
Port,
|
||||
enter_cell(Port)
|
||||
).
|
||||
Self := Caller.query,
|
||||
python_query( Self, Line ).
|
||||
|
||||
jupyter_consult(Text) :-
|
||||
blank( Text ),
|
||||
!.
|
||||
jupyter_consult(Cell) :-
|
||||
open_mem_read_stream( Cell, Stream),
|
||||
load_files(user:'jupyter cell',[stream(Stream)]).
|
||||
% Name = 'Inp',
|
||||
% stream_property(Stream, file_name(Name) ),
|
||||
load_files(user:'jupyter cell',[stream(Stream)]), !.
|
||||
%should load_files close?
|
||||
|
||||
blank(Text) :-
|
||||
@@ -62,31 +60,18 @@ blankc(' ').
|
||||
blankc('\n').
|
||||
blankc('\t').
|
||||
|
||||
enter_cell(retry) :-
|
||||
enter_cell(call).
|
||||
enter_cell(call) :-
|
||||
into_cell.
|
||||
enter_cell(fail) :-
|
||||
enter_cell(exit).
|
||||
enter_cell(answer) :-
|
||||
enter_cell(exit).
|
||||
enter_cell(exception(_)) :-
|
||||
enter_cell(exit).
|
||||
enter_cell(external_exception(_)).
|
||||
enter_cell(!).
|
||||
enter_cell(exit) :-
|
||||
nb_setval(jupyter_cell, off),
|
||||
close( user_output).
|
||||
|
||||
|
||||
into_cell :-
|
||||
nb_setval(jupyter_cell, on),
|
||||
open('/python/sys.input', read, _Input, [bom(false)]),
|
||||
open('/python/sys.stdout', append, _Output, []),
|
||||
open('/python/sys.stderr', append, _Error, []),
|
||||
set_prolog_flag(user_input,_Output),
|
||||
streams(false) :-
|
||||
% close( user_input),
|
||||
close( user_error ),
|
||||
close( user_output ).
|
||||
streams(true) :-
|
||||
% open('/python/input', read, _Input, [alias(user_input),bom(false)]),
|
||||
open('/python/sys.stdout', append, _Output, [alias(user_output)]),
|
||||
open('/python/sys.stderr', append, _Error, [alias(user_error)]),
|
||||
% set_prolog_flag(user_input,_Input),
|
||||
set_prolog_flag(user_output,_Output),
|
||||
set_prolog_flag(user_error,_Error).
|
||||
set_prolog_flag(user_error,_Error).
|
||||
|
||||
|
||||
completions(S, Self) :-
|
||||
@@ -185,7 +170,7 @@ predicate(N,P,A) :-
|
||||
cont(0, F, P, P0) :-
|
||||
atom_concat( F, P, P0 ).
|
||||
cont( _, F, P, PB ):-
|
||||
atom_concat( [F, P, '('], PB ).
|
||||
atom_concat( [F, P, '( )'], PB ).
|
||||
|
||||
|
||||
ready(_Self, Line ) :-
|
||||
|
@@ -3,15 +3,14 @@ import sys
|
||||
import abc
|
||||
import math
|
||||
import itertools
|
||||
import trace
|
||||
|
||||
|
||||
from typing import Iterator, List, Tuple, Iterable, Union
|
||||
from traitlets import Bool, Enum, observe, Int
|
||||
|
||||
try:
|
||||
from yap4py.yapi import Engine, Goal, EngineArgs, PrologTableIter
|
||||
except:
|
||||
print("Could not load _yap dll.")
|
||||
|
||||
from yap4py.yapi import *
|
||||
from yap_ipython.core.completer import Completer, Completion
|
||||
from yap_ipython.utils.strdispatch import StrDispatch
|
||||
# import yap_ipython.core
|
||||
@@ -32,12 +31,13 @@ bindvars = namedtuple('bindvars', 'list')
|
||||
library = namedtuple('library', 'list')
|
||||
v = namedtuple('_', 'slot')
|
||||
load_files = namedtuple('load_files', 'file ofile args')
|
||||
python_query= namedtuple('python_query', 'query_mgr string')
|
||||
python_query = namedtuple('python_query', 'query_mgr string')
|
||||
jupyter_query = namedtuple('jupyter_query', 'self text query')
|
||||
enter_cell = namedtuple('enter_cell', 'self' )
|
||||
exit_cell = namedtuple('exit_cell', 'self' )
|
||||
completions = namedtuple('completions', 'txt self' )
|
||||
errors = namedtuple('errors', 'self text' )
|
||||
streams = namedtuple('streams', ' text' )
|
||||
|
||||
|
||||
class YAPInputSplitter(InputSplitter):
|
||||
@@ -100,6 +100,7 @@ class YAPInputSplitter(InputSplitter):
|
||||
Python line."""
|
||||
t = self.physical_line_transforms + \
|
||||
[self.assemble_logical_lines] + self.logical_line_transforms
|
||||
return t
|
||||
|
||||
def engine(self, engine):
|
||||
self.yapeng = engine
|
||||
@@ -107,11 +108,11 @@ class YAPInputSplitter(InputSplitter):
|
||||
def validQuery(self, text, line=None):
|
||||
"""Return whether a legal query
|
||||
"""
|
||||
if text == self.shell.os:
|
||||
return True
|
||||
if not line:
|
||||
(_,line,_) = self.shell.prolog_cell(text)
|
||||
line = line.strip().rstrip()
|
||||
if not line:
|
||||
return False
|
||||
line = text.rstrip()
|
||||
(line, _, _, _)=self.shell.clean_end(line)
|
||||
self.errors = []
|
||||
self.yapeng.mgoal(errors(self, line),"user")
|
||||
return self.errors != []
|
||||
@@ -496,9 +497,11 @@ class YAPCompleter(Completer):
|
||||
"""
|
||||
self.matches = []
|
||||
prolog_res = self.shell.yapeng.mgoal(completions(text, self), "user")
|
||||
if self.matches:
|
||||
return text, self.matches
|
||||
magic_res = self.magic_matches(text)
|
||||
return text, magic_res
|
||||
|
||||
return text, self.matches+magic_res
|
||||
|
||||
|
||||
|
||||
@@ -509,8 +512,7 @@ class YAPRun:
|
||||
self.shell = shell
|
||||
self.yapeng = Engine()
|
||||
self.yapeng.goal(use_module(library("jupyter")))
|
||||
self.q = None
|
||||
self.port = "call"
|
||||
self.query = None
|
||||
self.os = None
|
||||
self.it = None
|
||||
self.shell.yapeng = self.yapeng
|
||||
@@ -521,41 +523,58 @@ class YAPRun:
|
||||
"""
|
||||
if not text:
|
||||
return []
|
||||
if text == self.os:
|
||||
return self.errors
|
||||
self.errors=[]
|
||||
(text,_,_,_) = self.clean_end(text)
|
||||
self.yapeng.mgoal(errors(self,text),"user")
|
||||
return self.errors
|
||||
|
||||
def jupyter_query(self, s):
|
||||
#
|
||||
# construct a self.query from a one-line string
|
||||
# self.q is opaque to Python
|
||||
program,query,mx = self.prolog_cell(s)
|
||||
Found = False
|
||||
|
||||
if query != self.os:
|
||||
self.os = None
|
||||
self.iterations = 0
|
||||
pg = jupyter_query( self, program, query)
|
||||
self.it = Goal( self.yapeng, pg)
|
||||
else:
|
||||
mx += self.iterations
|
||||
self.os = s
|
||||
for answ in self.it:
|
||||
found = True
|
||||
self.bindings += [answ]
|
||||
self.iterations += 1
|
||||
if mx == self.iterations:
|
||||
return True, self.bindings
|
||||
port = self.it.port
|
||||
if port == "exit":
|
||||
self.q = None
|
||||
self.os = None
|
||||
return True,self.bindings
|
||||
if port == "fail":
|
||||
self.q = none
|
||||
self.os = None
|
||||
if self.bindings_message:
|
||||
return True,self.bindings
|
||||
# construct a self.queryuery from a one-line string
|
||||
# self.query is opaque to Python
|
||||
try:
|
||||
program,query,stop,howmany = self.prolog_cell(s)
|
||||
found = False
|
||||
if s != self.os:
|
||||
self.os = s
|
||||
self.iterations = 0
|
||||
self.bindings = []
|
||||
pg = jupyter_query( self, program, query)
|
||||
self.query = self.yapeng.query( pg)
|
||||
self.query.port = "call"
|
||||
else:
|
||||
self.query.port = "retry"
|
||||
self.os = s
|
||||
howmany += self.iterations
|
||||
while self.query.next():
|
||||
answer = self.query.answer
|
||||
found = True
|
||||
self.bindings += [answer]
|
||||
self.iterations += 1
|
||||
if stop and howmany == self.iterations:
|
||||
self.query.close()
|
||||
self.query = None
|
||||
self.os = None
|
||||
return True, self.bindings
|
||||
if self.query.port == "exit":
|
||||
self.query.close()
|
||||
self.query = None
|
||||
self.os = None
|
||||
sys.stderr.writeln('Done, with', self.bindings)
|
||||
return True,self.bindings
|
||||
self.query.close()
|
||||
self.query = None
|
||||
self.os = None
|
||||
if self.bindings:
|
||||
sys.stderr.write('Done, with', self.bindings, '\n')
|
||||
else:
|
||||
sys.stderr.write('Fail\n')
|
||||
return True,{}
|
||||
except Exception as e:
|
||||
has_raised = True
|
||||
self.result.result = False
|
||||
|
||||
|
||||
def _yrun_cell(self, raw_cell, store_history=True, silent=False,
|
||||
@@ -573,7 +592,7 @@ class YAPRun:
|
||||
IPython's machinery, this
|
||||
should be set to False.
|
||||
silent : bool
|
||||
v If True, avoid side-effects, such as implicit displayhooks and
|
||||
If True, avoid side-effects, such as implicit displayhooks and
|
||||
and logging. silent=True forces store_history=False.
|
||||
shell_futures : bool
|
||||
If True, the code will share future statements with the interactive
|
||||
@@ -704,12 +723,31 @@ v If True, avoid side-effects, such as implicit displayhooks and
|
||||
has_raised = False
|
||||
try:
|
||||
state = None
|
||||
self.shell.bindings = dict = {}
|
||||
if cell.strip():
|
||||
state = self.jupyter_query( cell )
|
||||
self.bindings = dicts = []
|
||||
if cell.strip('\n \t'):
|
||||
#create a Trace object, telling it what to ignore, and whether to
|
||||
# do tracing or line-counting or both.
|
||||
tracer = trace.Trace(
|
||||
#ignoredirs=[sys.prefix, sys.exec_prefix],
|
||||
trace=1,
|
||||
count=0)
|
||||
|
||||
def f(self, cell):
|
||||
self.jupyter_query( cell )
|
||||
|
||||
# run the new command using the given tracer
|
||||
#
|
||||
try:
|
||||
self.yapeng.mgoal(streams(True),"user")
|
||||
#state = tracer.runfunc(f,self,cell)
|
||||
state = self.jupyter_query( cell )
|
||||
self.yapeng.mgoal(streams(False),"user")
|
||||
except Exception as e:
|
||||
has_raised = True
|
||||
self.yapeng.mgoal(streams("off"),"user")
|
||||
if state:
|
||||
self.shell.last_execution_succeeded = True
|
||||
self.result.result = (True, dict)
|
||||
self.result.result = (True, dicts)
|
||||
else:
|
||||
self.shell.last_execution_succeeded = True
|
||||
self.result.result = (True, {})
|
||||
@@ -735,50 +773,49 @@ v If True, avoid side-effects, such as implicit displayhooks and
|
||||
|
||||
return self.result
|
||||
|
||||
def clean_end(self,s):
|
||||
"""
|
||||
Look at the query suffix and return
|
||||
- whatever is left
|
||||
- how much was taken
|
||||
- whether to stop
|
||||
- when to stop
|
||||
"""
|
||||
i = -1
|
||||
try:
|
||||
its = 0
|
||||
j = 1
|
||||
while s[i].isdigit():
|
||||
ch = s[i]
|
||||
its += j * (ord(ch) - ord('0'))
|
||||
i-=1
|
||||
j *= 10;
|
||||
if s[i] == ';':
|
||||
if j > 1 or its != 0:
|
||||
return s[:i], 0 - i, True, its
|
||||
return s[:i], 0 - i, False, 0
|
||||
# one solution, stop
|
||||
return s, 0, True, 1
|
||||
except:
|
||||
return s,0, False, 0
|
||||
|
||||
|
||||
|
||||
def prolog_cell(self,s):
|
||||
"""
|
||||
Trasform a text into program+query. A query is the
|
||||
last line if the last line is non-empty and does not terminate
|
||||
on a dot. You can also finish with
|
||||
|
||||
- `*`: you request all solutions
|
||||
- '^': you want to check if there is an answer
|
||||
- '?'[N]: you want an answer; optionally you want N answers
|
||||
- `;`: you request all solutions
|
||||
- ';'[N]: you want an answer; optionally you want N answers
|
||||
|
||||
If the line terminates on a `*/` or starts on a `%` we assume the line
|
||||
is a comment.
|
||||
"""
|
||||
s = s.rstrip()
|
||||
take = 0
|
||||
its = 0
|
||||
s0 = ''
|
||||
for c in s:
|
||||
if c == '\n' or c.isspace():
|
||||
s0 += c
|
||||
break
|
||||
sf = ''
|
||||
for c in reversed(s):
|
||||
if c == '\n' or c.isspace():
|
||||
sf += c
|
||||
break
|
||||
[program,x,query] = s.rpartition('\n')
|
||||
if query == '':
|
||||
query = program
|
||||
while take < len(query):
|
||||
take += 1
|
||||
ch = query[-take]
|
||||
if ch.isdigit():
|
||||
its = its*10 + ord(ch) - ord('0')
|
||||
elif ch == '*' and take == 1:
|
||||
return program, query[:-take], -1
|
||||
elif ch == '.' and take == 1:
|
||||
return s, '', 1
|
||||
elif ch == '/' and query[-2] == '*' and take == 1:
|
||||
return program, query[:-take], 1
|
||||
elif ch == '^' and take == 1:
|
||||
return program, query[:-take], 1
|
||||
elif ch == '?':
|
||||
return program, query[:-take], its+1
|
||||
else:
|
||||
return program, query, 1
|
||||
return s, '', 1
|
||||
s0 = s.rstrip(' \n\t\i')
|
||||
[program,x,query] = s0.rpartition('\n')
|
||||
if query[-1] == '.':
|
||||
return s,'',False,0
|
||||
(query, _,loop, sols) = self.clean_end(query)
|
||||
return (program, query, loop, sols)
|
||||
|
Reference in New Issue
Block a user