-
Notifications
You must be signed in to change notification settings - Fork 43
/
p.q
120 lines (109 loc) · 5.58 KB
/
p.q
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
\d .p
version:@[{EMBEDPYVERSION};0;`development];
o:first string .z.o;
$[o="w";if[3.6>.z.K;'`$"kdb+ version must be 3.6+"];if[3.5>.z.K;'`$"kdb+ version must be 3.5+"]];
if[not .P.loaded:-1h=type@[`.p@;`numpy;`];
sc:{"'",x,"'.join([__import__('sysconfig').get_config_var(v)for v in",ssr[.j.j y;"\"";"'"],"])"};pr:{"print(",x,");"};
c:"-c \"",pr["'.'.join([str(getattr(__import__('sys').version_info,x))for x in ['major','minor']])"],"\"2>",$[o="w";"nul <nul";"/dev/null"];
if[(o="w")and any 3 6>"J"$"." vs first@[system"python3 ",;c;{system"python ",c}];'"embedPy requires python 3.6 or higher on windows"];
c:"-c \"",pr[$[o="w";sc["/python";`BINDIR`VERSION];sc["/libpython";`LIBDIR`LDVERSION]],"+'",$[o="w";".dll";o="l";".so";".dylib"],"'"],pr["__import__('sys').base_prefix"],pr["__import__('sys').prefix"],pr["__import__('sys').executable"],"\"2>",$[o="w";"nul <nul";"/dev/null"];
`L`H`P`B set'@[system"python3 ",;c;{system"python ",c}];
.P.env:not H~P;
.p:(`:./p 2:(`init;3))[L;H;B]]
loaded:.P.loaded
if[not loaded;
ei:{eo y _ x;n set .p.get[n:`$(2+x)_(y?"(")#y]value y x;};
eo:.p.e;
e:{$["def"~3#x;$[x[3]in"<*>";ei 3;eo];"class"~5#x;$[x[5]in"*>";ei 5;eo];eo]x}
];
k)c:{'[y;x]}/|: / compose list of functions
k)ce:{'[y;x]}/enlist,|: / compose with enlist (for variadic functions)
/ Aliases
if[not loaded;set'[`pyget`pyeval`pyimport;.p.get,.p.eval,import]];
qeval:c`.p.py2q,pyeval
/ Wrapper for foreigns
embedPy:{[f;x]
$[-11h<>t:type x0:x 0;
$[t=102h;
$[any u:x0~/:(*;<;>);
[c:(wrap;py2q;::)where[u]0;$[1=count x;.p.c c,;c .[;1_x]@]pyfunc f]; / call type
(:)~x0;[setattr . f,@[;0;{`$_[":"=s 0]s:string x}]1_x;];
(@)~x0;$[count 2_x;.[;2_x];]wrap call[getattr[f;`$"__getitem__"];enlist x 1;()!()];
(=)~x0;[call[getattr[f;`$"__setitem__"];raze 1_x;()!()];];
'`NYI];
wrap pyfunc[f]. x];
":"~first a0:string x0; / attr lookup and possible call
$[1=count x;;.[;1_x]]wrap f getattr/` vs`$1_a0;
x0~`.;f;x0~`;py2q f; / extract as foreign or q
wrap pyfunc[f]. x]} / default, call
unwrap:{$[i.isw x;x`.;x]}
xunwrap:{$[0=t:type x;.z.s each x;98=t;flip .z.s flip x;99=t;.z.s[key x]!.z.s value x;unwrap x]}
wfunc:{[f;x]r:wrap f x 0;$[count x:1_x;.[;x];]r}
i.wf:{[f;x]embedPy[f;x]}
wrap:ce i.wf@
import:ce wfunc pyimport
.p.eval:ce wfunc pyeval
.p.get:ce wfunc pyget
.p.set:{[f;x;y]f[x]unwrap y;}.p.set
`key`value set'{list$[i.isf y;wrap;i.isw y;;'`type][y][x][]}@'`:keys`:values;
.p.callable:{$[i.isw x;x;i.isf x;wrap[x];'`type]}
.p.pycallable:{$[i.isw x;x(>);i.isf x;wrap[x](>);'`type]}
.p.qcallable:{$[i.isw x;x(<);i.isf x;wrap[x](<);'`type]}
/ is foreign, wrapped, callable
i.isf:isp
i.isw:{$[105=type x;i.wf~$[104=type u:first get x;first get u;0b];0b]}
i.isc:{$[105=type y;$[x~y;1b;.z.s[x]last get y];0b]}ce 1#`.p.q2pargs
setattr:{[f;x;y;z]f[x;y;z];}import[`builtins]`:setattr
/ Calling python functions
pyfunc:{if[not i.isf x;'`type];ce .[.p.call x],`.p.q2pargs}
q2pargs:{
if[x~enlist(::);:(();()!())]; / zero args
hd:(k:i.gpykwargs x)0;
al:neg[hd]_(a:i.gpyargs x)0;
if[any 1_prev[u]and not u:i.isarg[i.kw]each neg[hd]_x;'"keywords last"]; / check arg order
cn:{$[()~x;x;11<>type x;'`type;x~distinct x;x;'`dupnames]};
:(unwrap each x[where not[al]¬ u],a 1;cn[named[;1],key k 1]!unwrap each(named:get'[(x,(::))where u])[;2],value k 1)
}
.q.pykw:{x[y;z]}i.kw:(`..pykw;;;) / identify keyword args with `name pykw value
.q.pyarglist:{x y}i.al:(`..pyas;;) / identify pos arg list (*args in python)
.q.pykwargs: {x y}i.ad:(`..pyks;;) / identify keyword dict (**kwargs in python)
i.gpykwargs:{dd:(0#`)!();
$[not any u:i.isarg[i.ad]each x;(0;dd);not last u;'"pykwargs last";
1<sum u;'"only one pykwargs allowed";(1;dd,get[x where[u]0]1)]}
i.gpyargs:{$[not any u:i.isarg[i.al]each x;(u;());1<sum u;'"only one pyargs allowed";(u;(),get[x where[u]0]1)]}
i.isarg:{$[104=type y;x~first get y;0b]} / y is python argument identifier x
/ Help & Print
gethelp:{[h;x]$[i.isf x;h x;i.isw x;h x`.;i.isc x;h x{get[x]y}/1 0 1 1;"no help available"]}
repr:gethelp import[`builtins;`:repr;<]
help:{[gh;h;x]if[10=type u:gh[h]x;-2 u]}[gethelp]import[`builtins;`:help]
helpstr:gethelp import[`inspect;`:getdoc;<]
print:{x y;}import[`builtins]`:print
{@[`.;x;:;get x]}each`help`print; / comment to remove from global namespace
/ Closures
p)def qclosure(func,*state):
def cfunc(a0=None,*args):
nonlocal state
res=func(*state+(a0,)+args)
state=(res[0],)
return res[1]
return cfunc
closure:.p.get[`qclosure] / implement as: closure[{[state;dummy] ...;(newState;result)};initState]
/ Generators
p)import itertools
i.gl:.p.eval["lambda f,n:(f(x)for x in(itertools.count()if n==None else range(n)))"][>]
generator:{[f;i;n]i.gl[closure[f;i]`.;n]}
/ Add cwd and $QHOME to sys.path
sp:.p.import[`sys]`:path
spq:distinct("";getenv`QHOME),sp`
sp[`:clear][];
sp[`:extend]spq;
/ write python stdout/err to 1 and 2
if[not@[{count key .pyq};::;0b];{.p.import[`sys;x][:;`:write;{x y;count y}y]}'[`:stdout`:stderr;1 2]];
/ set sys.argv
if[not .p.eval["hasattr";.p.import`sys;`argv]`;.p.import[`sys][:;`argv;enlist""]]
if[not loaded;if[not count .p.import[`sys][`:argv]`;.p.import[`sys][:;`:argv;enlist""]]]
/ Cleanup
{![`.p;();0b;x]}`getseq`ntolist`runs`wfunc`gethelp`sp`spq`loaded;
{@[`.p;x;:;.p.import[`builtins]hsym x]}each`tuple`list`dict`isinstance;
/ VirtualEnv warning for windows users
if[.P.env&.z.o like"w*";-2"Warning:\n\tVirtual Environments not supported for embedPy on Windows.\n\tUsing the 'BASE' version of Python, not the virtual environment Python";]