Merge 192.168.1.79:github/yap-6.3

This commit is contained in:
Vítor Santos Costa
2018-03-26 11:03:08 +01:00
119 changed files with 47950 additions and 1288 deletions

View File

@@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
"""
Created on Mon Jan 13 18:17:15 2014
@author: takluyver
"""
import sys
PY3 = (sys.version_info[0] >= 3)
try:
from inspect import signature, Parameter # Python >= 3.3
except ImportError:
from ._signatures import signature, Parameter
if PY3:
from functools import wraps
else:
from functools import wraps as _wraps
def wraps(f):
def dec(func):
_wraps(f)(func)
func.__wrapped__ = f
return func
return dec
def callback_prototype(prototype):
"""Decorator to process a callback prototype.
A callback prototype is a function whose signature includes all the values
that will be passed by the callback API in question.
The original function will be returned, with a ``prototype.adapt`` attribute
which can be used to prepare third party callbacks.
"""
protosig = signature(prototype)
positional, keyword = [], []
for name, param in protosig.parameters.items():
if param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD):
raise TypeError("*args/**kwargs not supported in prototypes")
if (param.default is not Parameter.empty) \
or (param.kind == Parameter.KEYWORD_ONLY):
keyword.append(name)
else:
positional.append(name)
kwargs = dict.fromkeys(keyword)
def adapt(callback):
"""Introspect and prepare a third party callback."""
sig = signature(callback)
try:
# XXX: callback can have extra optional parameters - OK?
sig.bind(*positional, **kwargs)
return callback
except TypeError:
pass
# Match up arguments
unmatched_pos = positional[:]
unmatched_kw = kwargs.copy()
unrecognised = []
# TODO: unrecognised parameters with default values - OK?
for name, param in sig.parameters.items():
# print(name, param.kind) #DBG
if param.kind == Parameter.POSITIONAL_ONLY:
if len(unmatched_pos) > 0:
unmatched_pos.pop(0)
else:
unrecognised.append(name)
elif param.kind == Parameter.POSITIONAL_OR_KEYWORD:
if (param.default is not Parameter.empty) and (name in unmatched_kw):
unmatched_kw.pop(name)
elif len(unmatched_pos) > 0:
unmatched_pos.pop(0)
else:
unrecognised.append(name)
elif param.kind == Parameter.VAR_POSITIONAL:
unmatched_pos = []
elif param.kind == Parameter.KEYWORD_ONLY:
if name in unmatched_kw:
unmatched_kw.pop(name)
else:
unrecognised.append(name)
else: # VAR_KEYWORD
unmatched_kw = {}
# print(unmatched_pos, unmatched_kw, unrecognised) #DBG
if unrecognised:
raise TypeError("Function {!r} had unmatched arguments: {}".format(callback, unrecognised))
n_positional = len(positional) - len(unmatched_pos)
@wraps(callback)
def adapted(*args, **kwargs):
"""Wrapper for third party callbacks that discards excess arguments"""
# print(args, kwargs)
args = args[:n_positional]
for name in unmatched_kw:
# XXX: Could name not be in kwargs?
kwargs.pop(name)
# print(args, kwargs, unmatched_pos, cut_positional, unmatched_kw)
return callback(*args, **kwargs)
return adapted
prototype.adapt = adapt
return prototype

View File

@@ -13,7 +13,7 @@ events and the arguments which will be passed to them.
This API is experimental in yap_ipython 2.0, and may be revised in future versions.
"""
from backcall import callback_prototype
from yap_ipython.core.backcall import callback_prototype
class EventManager(object):

View File

@@ -1,4 +1,4 @@
from backcall import callback_prototype
from yap_ipython.core.backcall import callback_prototype
import unittest
from unittest.mock import Mock
import nose.tools as nt

View File

@@ -0,0 +1,258 @@
/**
* @file jupyter.yap
*
* @brief allow interaction between Jupyter and YAP.
*
* @long The code in here:
* - establishes communication between Prolog and Python Streams
* - inputs Prolog code and queries
* - supports completion of Prolog programs.
* -
*/
%% :- module( jupyter,
%% [jupyter_query/3,
%% errors/2,
%% ready/2,
%% completion/2,
%% ]
%% ).
:- reexport(library(yapi)).
:- use_module(library(lists)).
:- use_module(library(maplist)).
:- use_module(library(python)).
:- python_import(sys).
jupyter_query(Self, Cell, Line ) :-
setup_call_cleanup(
enter_cell(Self),
jupyter_cell(Self, Cell, Line),
exit_cell(Self)
).
jupyter_cell(_Self, Cell, _) :-
jupyter_consult(Cell),
fail.
jupyter_cell( _Self, _, Line ) :-
blank( Line ),
!.
jupyter_cell( _Self, _, [] ) :- !.
jupyter_cell( Self, _, Line ) :-
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)]).
%should load_files close?
blank(Text) :-
atom_codes(Text, L),
maplist( blankc, L).
blankc(' ').
blankc('\n').
blankc('\t').
enter_cell(_Self) :-
%open('//python/input', read, _Input, []),
open('//python/sys.stdout', append, _Output, []),
open('//python/sys.stdout', append, _Error, []),
%set_prolog_flag(user_input, _Input),
set_prolog_flag(user_output, _Output),
set_prolog_flag(user_error, _Error).
exit_cell(_Self) :-
%close( user_input),
close( user_output),
close( user_error).
completions(S, Self) :-
open_mem_read_stream(S, St),
scan_to_list(St, Tokens),
close(St),
reverse(Tokens, RTokens),
strip_final_tokens(RTokens, MyTokens),
setof( Completion, complete(MyTokens, Completion), Cs),
Self.matches := Cs.
strip_final_tokens(['EOT'|Ts], Ts) :- !.
strip_final_tokens( Ts, Ts ).
complete([E,l,C,l,A|More],
isconsult(A),
%B = l,
library(C,Lib),
%D=l,
E=atom(Prefix),
\+ arg( Rest ),
check_library( Prefix, Lib, C).
complete([E,l,C,l,-,'['|More],
isconsult(A),
%B = l,
library(C,Lib),
%D=l,
E=atom(Prefix),
\+ arg( Rest ),
check_library( Prefix, Lib, C).
complete([C,l,A|More],
isconsult(A),
%B = l,
C=atom(Prefix),
\+ arg( Rest ),
file_or_library( Prefix, C).
complete([C,l,-,'['|More],
isconsult(A),
%B = l,
C=atom(Prefix),
\+ arg( Rest ),
file_or_library( Prefix, C).
complete( [atom(F)|Rest], C) :-
\+ arg( Rest ),
predicate( F, Pred, Arity ),
cont( Arity, F, Pred, C).
isconsult( atom(use_module) ).
isconsult( atom(ensure_loaded) ).
isconsult( atom(compile) ).
isconsult( atom(consult) ).
isconsult( atom(reconsult) ).
isconsult( atom(load_files) ).
isconsult( '[' ).
arg([']'|_]).
arg([l|_]).
file_or_library(F,C) :-
libsym(C0),
atom_cooncat(F,C,C0).
file_or_library(F,C) :-
check_file(F,C).
check_file(F0,C) :-
atom_concat('\'',F,F0),
!,
absolute_file_name( F, FF, [access(none)] ),
atom_concat( FF, '*' , Pat),
absolute_file_name( Pat, C0, [glob(true)] ),
atom_concat(Pat,C00,C0),
atom_conct(C00,'\'',C).
check_file(F0,C) :-
atom_concat( F0, '*' , Pat),
absolute_file_name( Pat, C0, [glob(true)] ),
atom_concat(Pat,C,C0).
check_library( Lib, F, C) :-
atom_concat( F, '*' , Pat),
LibF =.. [Lib(Pat)],
absolute_file_name( LibF, Lib, [glob(true)] ),
file_directory_name( Lib, Name),
( atom_concat(C, '.yap', Name) -> true ;
atom_concat(C, '.ypp', Name) -> true ;
atom_concat(C, '.prolog', Name) -> true
).
predicate(N,P,A) :-
system_predicate(P0/A),
atom_concat(N,P,P0).
predicate(N,P,A) :-
current_predicate(P0/A),
atom_concat(N,P,P0).
cont(0, F, P, P0) :-
atom_concat( F, P, P0 ).
cont( _, F, P, PB ):-
atom_concat( [F, P, '('], PB ).
ready(_Self, Line ) :-
blank( Line ),
!.
ready(Self, Line ) :-
errors( Self, Line ),
\+ syntax_error(_,_).
errors( Self, Text ) :-
setup_call_cleanup(
open_events( Self, Text, Stream),
clauses(Self, Stream),
close_events( Self )
).
clauses(Self, Stream) :-
repeat,
read_clause(Stream, Cl, [term_position(_Pos), syntax_errors(fail)] ),
command( Self, Cl ),
Cl == end_of_file,
!.
command(_, end_of_file) :- !.
command( _Self, ( :- op(Prio,Assoc,Name) ) ) :-
addop(Prio,Assoc,Name).
command( _Self, ( :- module(Name, Exports) )) :-
retract( active_module( M0 ) ),
atom_concat( '__m0_', Name, M ),
assert( active_module(M) ),
assert( undo( active_module(M0) ) ),
maplist( addop2(M), Exports).
addop(Prio,Assoc,Name) :-
(
current_op(OPrio, SimilarAssoc, Name),
op(Prio, Assoc, Name),
matched_op(Assoc, SimilarAssoc)
->
assertz( undo(op( OPrio, Assoc, Name ) ) )
;
assertz( undo(op( 0, Assoc, Name ) ) )
).
addop2(M, op(Prio, Assoc, Name)) :-
addop( Prio, Assoc, M:Name ).
matched_op(A, B) :-
optype( A, T),
optype( B, T).
optype(fx,pre).
optype(fy,pre).
optype(xfx,in).
optype(xfy,in).
optype(yfx,in).
optype(yfy,in).
optype(xf,pos).
optype(yf,pos).
:- dynamic user:portray_message/2.
:- multifile user:portray_message/2.
:- dynamic syntax_error/4, undo/1.
open_events(Self, Text, Stream) :-
Self.errors := [],
open_mem_read_stream( Text, Stream ),
assert((user:portray_message(_Severity, error(syntax_error(Cause),info(between(_,LN,_), _FileName, CharPos, Details))) :-
assert( syntax_error(Cause,LN,CharPos,Details) )
)).
close_events( _Self ) :-
retract( undo(G) ),
call(G),
fail.
close_events( Self ) :-
retract( syntax_error( C, L, N, A )),
Self.errors := [t(C,L,N,A)] + Self.errors,
fail.
close_events( _ ).

View File

@@ -0,0 +1,161 @@
"""prompt-toolkit utilities
Everything in this module is a private API,
not to be used outside yap_ipython.
"""
# Copyright (c) yap_ipython Development Team.
# Distributed under the terms of the Modified BSD License.
import unicodedata
from wcwidth import wcwidth
from yap_ipython.core.completer import (
provisionalcompleter, cursor_to_position,
_deduplicate_completions)
from prompt_toolkit.completion import Completer, Completion
from prompt_toolkit.layout.lexers import Lexer
from prompt_toolkit.layout.lexers import PygmentsLexer
import pygments.lexers as pygments_lexers
_completion_sentinel = object()
def _elide(string, *, min_elide=30):
"""
If a string is long enough, and has at least 2 dots,
replace the middle part with ellipses.
If three consecutive dots, or two consecutive dots are encountered these are
replaced by the equivalents HORIZONTAL ELLIPSIS or TWO DOT LEADER unicode
equivalents
"""
string = string.replace('...','\N{HORIZONTAL ELLIPSIS}')
string = string.replace('..','\N{TWO DOT LEADER}')
if len(string) < min_elide:
return string
parts = string.split('.')
if len(parts) <= 3:
return string
return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(parts[0], parts[1][0], parts[-2][-1], parts[-1])
def _adjust_completion_text_based_on_context(text, body, offset):
if text.endswith('=') and len(body) > offset and body[offset] is '=':
return text[:-1]
else:
return text
class IPythonPTCompleter(Completer):
"""Adaptor to provide yap_ipython completions to prompt_toolkit"""
def __init__(self, ipy_completer=None, shell=None, patch_stdout=None):
if shell is None and ipy_completer is None:
raise TypeError("Please pass shell=an InteractiveShell instance.")
self._ipy_completer = ipy_completer
self.shell = shell
if patch_stdout is None:
raise TypeError("Please pass patch_stdout")
self.patch_stdout = patch_stdout
@property
def ipy_completer(self):
if self._ipy_completer:
return self._ipy_completer
else:
return self.shell.Completer
def get_completions(self, document, complete_event):
if not document.current_line.strip():
return
# Some bits of our completion system may print stuff (e.g. if a module
# is imported). This context manager ensures that doesn't interfere with
# the prompt.
with self.patch_stdout(), provisionalcompleter():
body = document.text
cursor_row = document.cursor_position_row
cursor_col = document.cursor_position_col
cursor_position = document.cursor_position
offset = cursor_to_position(body, cursor_row, cursor_col)
yield from self._get_completions(body, offset, cursor_position, self.ipy_completer)
@staticmethod
def _get_completions(body, offset, cursor_position, ipyc):
"""
Private equivalent of get_completions() use only for unit_testing.
"""
debug = getattr(ipyc, 'debug', False)
completions = _deduplicate_completions(
body, ipyc.completions(body, offset))
for c in completions:
if not c.text:
# Guard against completion machinery giving us an empty string.
continue
text = unicodedata.normalize('NFC', c.text)
# When the first character of the completion has a zero length,
# then it's probably a decomposed unicode character. E.g. caused by
# the "\dot" completion. Try to compose again with the previous
# character.
if wcwidth(text[0]) == 0:
if cursor_position + c.start > 0:
char_before = body[c.start - 1]
fixed_text = unicodedata.normalize(
'NFC', char_before + text)
# Yield the modified completion instead, if this worked.
if wcwidth(text[0:1]) == 1:
yield Completion(fixed_text, start_position=c.start - offset - 1)
continue
# TODO: Use Jedi to determine meta_text
# (Jedi currently has a bug that results in incorrect information.)
# meta_text = ''
# yield Completion(m, start_position=start_pos,
# display_meta=meta_text)
display_text = c.text
adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset)
if c.type == 'function':
yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()'), display_meta=c.type+c.signature)
else:
yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text), display_meta=c.type)
class IPythonPTLexer(Lexer):
"""
Wrapper around PythonLexer and BashLexer.
"""
def __init__(self):
l = pygments_lexers
self.python_lexer = PygmentsLexer(l.Python3Lexer)
self.shell_lexer = PygmentsLexer(l.BashLexer)
self.magic_lexers = {
'HTML': PygmentsLexer(l.HtmlLexer),
'html': PygmentsLexer(l.HtmlLexer),
'javascript': PygmentsLexer(l.JavascriptLexer),
'js': PygmentsLexer(l.JavascriptLexer),
'perl': PygmentsLexer(l.PerlLexer),
'ruby': PygmentsLexer(l.RubyLexer),
'latex': PygmentsLexer(l.TexLexer),
'prolog': PygmentsLexer(l.PrologLexer),
}
def lex_document(self, cli, document):
text = document.text.lstrip()
lexer = self.python_lexer
if text.startswith('!') or text.startswith('%%bash'):
lexer = self.shell_lexer
elif text.startswith('%%'):
for magic, l in self.magic_lexers.items():
if text.startswith('%%' + magic):
lexer = l
break
return lexer.lex_document(cli, document)

View File

@@ -562,7 +562,7 @@ class YAPRun:
self.os = None
sys.stderr.writeln('Done, with', self.bindings)
return True,self.bindings
if self.bindings:
if found:
sys.stderr.write('Done, with', self.bindings, '\n')
else:
self.query.close()
@@ -571,8 +571,9 @@ class YAPRun:
sys.stderr.write('Fail\n')
return True,{}
except Exception as e:
sys.stderr.write('Exception after', self.bindings, '\n')
has_raised = True
self.result.result = False
return False,{}
def _yrun_cell(self, raw_cell, store_history=True, silent=False,