forked from KxSystems/embedPy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
p.q
128 lines (115 loc) · 5.99 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
121
122
123
124
125
126
127
\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[(o="w")&count getenv[`UNDER_PYTHON];'"embedPy running within a Python process not supported for Windows"];
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 `3.6>`$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";]
/ Add warning for unsupported numpy 1.22
{np:@[.p.import;`numpy;{(::)}];if[np~(::);:np];
if[any 1 21<"J"$-1_vs["."]np[`:__version__]`;
-2"Warning:\n\tDue to a bug in the numpy C api, conversions between q and numpy objects for 'numpy>=1.22'\n\tare unsupported at this time. Please downgrade your numpy version.";]
}`