pykx 4.2.0__cp315-cp315-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. pykx/__init__.py +586 -0
  2. pykx/_ipc.c +13916 -0
  3. pykx/_ipc.cp315-win_amd64.pyd +0 -0
  4. pykx/_ipc.pxd +10 -0
  5. pykx/_ipc.pyx +103 -0
  6. pykx/_numpy.c +171 -0
  7. pykx/_numpy.cp315-win_amd64.pyd +0 -0
  8. pykx/_pyarrow.py +68 -0
  9. pykx/_pykx_helpers.c +35 -0
  10. pykx/_pykx_helpers.cp315-win_amd64.pyd +0 -0
  11. pykx/_streamlit_conn.py +238 -0
  12. pykx/_tcore.c +622 -0
  13. pykx/_version.py +21 -0
  14. pykx/_wrappers.c +51599 -0
  15. pykx/_wrappers.cp315-win_amd64.pyd +0 -0
  16. pykx/_wrappers.pxd +15 -0
  17. pykx/_wrappers.pyx +649 -0
  18. pykx/cast.py +137 -0
  19. pykx/compress_encrypt.py +193 -0
  20. pykx/config.py +651 -0
  21. pykx/console.py +68 -0
  22. pykx/constants.py +11 -0
  23. pykx/core.c +25650 -0
  24. pykx/core.cp315-win_amd64.pyd +0 -0
  25. pykx/core.pxd +114 -0
  26. pykx/core.pyx +518 -0
  27. pykx/ctx.py +265 -0
  28. pykx/db.py +1348 -0
  29. pykx/docs/api/pykx-execution/q.md +2839 -0
  30. pykx/embedded_q.py +268 -0
  31. pykx/exceptions.py +97 -0
  32. pykx/extensions/chained_tick.q +39 -0
  33. pykx/extensions/dashboards.q +129 -0
  34. pykx/extensions/gateway.q +44 -0
  35. pykx/extensions/hdb.q +18 -0
  36. pykx/extensions/plant.q +77 -0
  37. pykx/extensions/rdb.q +49 -0
  38. pykx/help.py +215 -0
  39. pykx/include/foreign.h +55 -0
  40. pykx/include/k.h +196 -0
  41. pykx/include/py.h +197 -0
  42. pykx/include/unlicensed_getitem.h +34 -0
  43. pykx/include/vector_conversion.h +30 -0
  44. pykx/ipc.py +3141 -0
  45. pykx/lib/bq.q_ +0 -0
  46. pykx/lib/csvutil.q +137 -0
  47. pykx/lib/dbmaint.q +158 -0
  48. pykx/lib/html.q +94 -0
  49. pykx/lib/kurl.q_ +0 -0
  50. pykx/lib/kurl.sidecar.q_ +0 -0
  51. pykx/lib/kxic.k +23 -0
  52. pykx/lib/l64/.gitkeep +0 -0
  53. pykx/lib/l64/kxreaper +0 -0
  54. pykx/lib/l64/libe.so +0 -0
  55. pykx/lib/l64/libkurl.so +0 -0
  56. pykx/lib/l64/libobjstor.so +0 -0
  57. pykx/lib/l64/libq.so +0 -0
  58. pykx/lib/l64/pg +0 -0
  59. pykx/lib/l64arm/.gitkeep +0 -0
  60. pykx/lib/l64arm/libe.so +0 -0
  61. pykx/lib/l64arm/libq.so +0 -0
  62. pykx/lib/licmet.q_ +0 -0
  63. pykx/lib/m64/.gitkeep +0 -0
  64. pykx/lib/m64/libe.so +0 -0
  65. pykx/lib/m64/libq.dylib +0 -0
  66. pykx/lib/m64arm/.gitkeep +0 -0
  67. pykx/lib/m64arm/libe.so +0 -0
  68. pykx/lib/m64arm/libq.dylib +0 -0
  69. pykx/lib/objstor.q_ +5 -0
  70. pykx/lib/pykx.q +2033 -0
  71. pykx/lib/pykx_init.q_ +0 -0
  72. pykx/lib/qlog.q_ +0 -0
  73. pykx/lib/read.q +46 -0
  74. pykx/lib/rest.q_ +0 -0
  75. pykx/lib/sql.k_ +0 -0
  76. pykx/lib/w64/.gitkeep +0 -0
  77. pykx/lib/w64/e.dll +0 -0
  78. pykx/lib/w64/e.lib +0 -0
  79. pykx/lib/w64/q.dll +0 -0
  80. pykx/lib/w64/qdll.lib +0 -0
  81. pykx/lib/write.q +44 -0
  82. pykx/license.py +231 -0
  83. pykx/module.py +503 -0
  84. pykx/nbextension.py +207 -0
  85. pykx/numpy_conversions.c +62 -0
  86. pykx/numpy_conversions.cp315-win_amd64.pyd +0 -0
  87. pykx/p.k +1 -0
  88. pykx/pandas_api/__init__.py +114 -0
  89. pykx/pandas_api/pandas_apply.py +79 -0
  90. pykx/pandas_api/pandas_conversions.py +412 -0
  91. pykx/pandas_api/pandas_indexing.py +883 -0
  92. pykx/pandas_api/pandas_map.py +94 -0
  93. pykx/pandas_api/pandas_merge.py +592 -0
  94. pykx/pandas_api/pandas_meta.py +1119 -0
  95. pykx/pandas_api/pandas_native_agg.py +192 -0
  96. pykx/pandas_api/pandas_replace.py +47 -0
  97. pykx/pandas_api/pandas_reset_index.py +84 -0
  98. pykx/pandas_api/pandas_set_index.py +71 -0
  99. pykx/pandas_api/pandas_sorting.py +161 -0
  100. pykx/pykx.c +830 -0
  101. pykx/pykx.dll +0 -0
  102. pykx/pykx.exp +0 -0
  103. pykx/pykx.lib +0 -0
  104. pykx/pykx.q +2033 -0
  105. pykx/pykx_init.q_ +0 -0
  106. pykx/pykxq.c +830 -0
  107. pykx/pykxq.dll +0 -0
  108. pykx/pykxq.exp +0 -0
  109. pykx/pykxq.lib +0 -0
  110. pykx/python.c +23 -0
  111. pykx/q.so/libs/kdbx/l64/kxreaper +0 -0
  112. pykx/q.so/libs/kdbx/l64/libkurl.so +0 -0
  113. pykx/q.so/libs/kdbx/l64/libobjstor.so +0 -0
  114. pykx/q.so/libs/kdbx/l64/libq.so +0 -0
  115. pykx/q.so/libs/kdbx/l64/pg +0 -0
  116. pykx/q.so/libs/kdbx/l64/symbols.txt +50 -0
  117. pykx/q.so/libs/kdbx/l64arm/libq.so +0 -0
  118. pykx/q.so/libs/kdbx/m64/libq.dylib +0 -0
  119. pykx/q.so/libs/kdbx/m64arm/libq.dylib +0 -0
  120. pykx/q.so/libs/kdbx/w64/q.dll +0 -0
  121. pykx/q.so/libs/kdbx/w64/qdll.lib +0 -0
  122. pykx/q.so/qk/bq.q_ +0 -0
  123. pykx/q.so/qk/kurl.q_ +0 -0
  124. pykx/q.so/qk/kurl.sidecar.q_ +0 -0
  125. pykx/q.so/qk/licmet.q_ +0 -0
  126. pykx/q.so/qk/objstor.q_ +5 -0
  127. pykx/q.so/qk/pykx_init.q_ +0 -0
  128. pykx/q.so/qk/qlog.q_ +0 -0
  129. pykx/q.so/qk/rest.q_ +0 -0
  130. pykx/q.so/qk/sql.k_ +0 -0
  131. pykx/query.py +1034 -0
  132. pykx/random.py +129 -0
  133. pykx/read.py +341 -0
  134. pykx/register.py +166 -0
  135. pykx/reimporter.py +73 -0
  136. pykx/remote.py +236 -0
  137. pykx/schema.py +170 -0
  138. pykx/serialize.py +150 -0
  139. pykx/streamlit.py +24 -0
  140. pykx/system.py +524 -0
  141. pykx/tick.py +1811 -0
  142. pykx/toq.c +64075 -0
  143. pykx/toq.cp315-win_amd64.pyd +0 -0
  144. pykx/toq.pyx +3218 -0
  145. pykx/util.py +844 -0
  146. pykx/wrappers.py +11922 -0
  147. pykx/write.py +196 -0
  148. pykx-4.2.0.dist-info/LICENSE.txt +265 -0
  149. pykx-4.2.0.dist-info/METADATA +647 -0
  150. pykx-4.2.0.dist-info/RECORD +152 -0
  151. pykx-4.2.0.dist-info/WHEEL +5 -0
  152. pykx-4.2.0.dist-info/top_level.txt +1 -0
pykx/__init__.py ADDED
@@ -0,0 +1,586 @@
1
+ """
2
+ An interface between Python and q.
3
+ """
4
+
5
+ import base64
6
+ import logging
7
+ import os
8
+ import sys
9
+
10
+ if os.getenv('PYKX_LOADING_Q', '') == '':
11
+ os.environ['PYKX_LOADING_PYTHON'] = 'true'
12
+
13
+ if os.getenv('PYKX_OLD_QHOME', '') != '' and os.getenv('PYKX_LOADING_PYTHON', '') != '':
14
+ os.environ['QHOME'] = os.getenv('PYKX_OLD_QHOME', "")
15
+ if os.getenv('PYKX_OLD_QPATH', '') != '' and os.getenv('PYKX_LOADING_PYTHON', '') != '':
16
+ os.environ['QPATH'] = os.getenv('PYKX_OLD_QPATH', "")
17
+
18
+ if os.getenv('PYKX_LOADED_UNDER_Q') == 'True':
19
+ sys.exit(0)
20
+
21
+ # Attempt to import PyArrow early to get ahead of others (e.g. Pandas) who would try to import it
22
+ # without guarding against segfaults. Skip this if we're just doing a qinit check.
23
+ if os.getenv('PYKX_QINIT_CHECK', '') == '':
24
+ try:
25
+ from ._pyarrow import pyarrow
26
+ except ImportError: # nocov
27
+ pass
28
+ else: # nocov
29
+ pass
30
+
31
+
32
+ # List of beta features available in the current KDB-X Python version
33
+ beta_features = []
34
+
35
+
36
+ from . import reimporter
37
+ # Importing core initializes q if in licensed mode, and loads the q C API symbols. This should
38
+ # happen early on so that if the qinit check is currently happening then no time is wasted.
39
+ # The current process will exit while the core module is loading if the qinit check is happening.
40
+ from . import core
41
+
42
+ from abc import ABCMeta, abstractmethod
43
+ import itertools as it
44
+ from pathlib import Path
45
+ import platform
46
+ import shutil
47
+ import signal
48
+ from typing import Any, List, Optional, Union
49
+ from warnings import warn
50
+ from weakref import proxy
51
+
52
+ from .config import k_allocator, licensed, no_pykx_signal, pykx_lib_dir, pykx_platlib_dir, under_q
53
+ from . import util
54
+
55
+ if platform.system() == 'Windows': # nocov
56
+ os.environ['PATH'] += f';{pykx_platlib_dir}'
57
+ if platform.python_version_tuple()[:2] >= ('3', '8'):
58
+ os.add_dll_directory(pykx_platlib_dir)
59
+
60
+ # Cache initialised signal values prior to KDB-X Python loading
61
+ _signal_list = [
62
+ 'signal.SIGINT',
63
+ 'signal.SIGTERM',
64
+ ]
65
+
66
+ _signal_dict = {}
67
+
68
+ for i in _signal_list:
69
+ _signal_dict[i] = signal.getsignal(eval(i))
70
+
71
+
72
+ def _first_resolved_path(possible_paths: List[Union[str, Path]]) -> Path:
73
+ """Returns the resolved version of the first path that exists."""
74
+ for path in possible_paths:
75
+ try:
76
+ return Path(path).resolve(strict=True)
77
+ except FileNotFoundError:
78
+ pass
79
+ unfound_paths = '\n'.join(str(path) for path in possible_paths)
80
+ raise FileNotFoundError(f'Could not find any of the following files:\n{unfound_paths}')
81
+
82
+
83
+ class Q(metaclass=ABCMeta):
84
+ """Abstract base class for all interfaces between Python and q.
85
+
86
+ See Also:
87
+ - [`pykx.EmbeddedQ`][pykx.EmbeddedQ]
88
+ - [`pykx.QConnection`][pykx.QConnection]
89
+ """
90
+ reserved_words = {
91
+ 'abs', 'acos', 'aj', 'aj0', 'ajf', 'ajf0', 'all', 'and', 'any', 'asc',
92
+ 'asin', 'asof', 'atan', 'attr', 'avg', 'avgs', 'bin',
93
+ 'binr', 'ceiling', 'cols', 'cor', 'cos', 'count', 'cov', 'cross',
94
+ 'csv', 'cut', 'delete', 'deltas', 'desc', 'dev', 'differ', 'distinct',
95
+ 'div', 'do', 'dsave', 'each', 'ej', 'ema', 'enlist', 'eval',
96
+ 'except', 'exec', 'exit', 'exp', 'fby', 'fills', 'first', 'fkeys',
97
+ 'flip', 'floor', 'get', 'getenv', 'group', 'gtime', 'hclose', 'hcount', 'hdel',
98
+ 'hopen', 'hsym', 'iasc', 'idesc', 'if', 'ij', 'ijf', 'in',
99
+ 'inter', 'inv', 'key', 'keys', 'last', 'like', 'lj', 'ljf',
100
+ 'load', 'log', 'lower', 'lsq', 'ltime', 'ltrim', 'mavg', 'max', 'maxs',
101
+ 'mcount', 'md5', 'mdev', 'med', 'meta', 'min', 'mins', 'mmax', 'mmin', 'mmu', 'mod',
102
+ 'msum', 'neg', 'next', 'not', 'null', 'or', 'over', 'parse', 'peach', 'pj', 'prd',
103
+ 'prds', 'prev', 'prior', 'rand', 'rank', 'ratios', 'raze', 'read0', 'read1', 'reciprocal',
104
+ 'reval', 'reverse', 'rload', 'rotate', 'rsave', 'rtrim', 'save', 'scan', 'scov', 'sdev',
105
+ 'select', 'set', 'setenv', 'show', 'signum', 'sin',
106
+ 'sqrt', 'ss', 'ssr', 'string', 'sublist', 'sum', 'sums', 'sv', 'svar',
107
+ 'system', 'tables', 'tan', 'til', 'trim', 'type', 'uj', 'ujf', 'ungroup',
108
+ 'union', 'update', 'upper', 'value', 'var', 'view', 'views',
109
+ 'vs', 'wavg', 'where', 'while', 'within', 'wj', 'wj1',
110
+ 'wsum', 'xasc', 'xbar', 'xcol', 'xcols', 'xdesc', 'xexp', 'xgroup', 'xkey', 'xlog',
111
+ 'xprev', 'xrank'
112
+ }
113
+
114
+ operators = {
115
+ 'drop': '_',
116
+ 'coalesce': '^', 'fill': '^',
117
+ 'take': '#', 'set_attribute': '#',
118
+ 'join': ',',
119
+ 'find': '?', 'enum_extend': '?', 'roll': '?', 'deal': '?',
120
+ 'dict': '!', 'enkey': '!', 'unkey': '!', 'enumeration': '!',
121
+ 'enumerate': '$', 'pad': '$', 'cast': '$', 'tok': '$',
122
+ 'compose': '\''
123
+ }
124
+
125
+ def __init__(self):
126
+ self.paths = default_paths
127
+ # Start with an empty set to avoid errors during initialization.
128
+ object.__setattr__(self, '_q_ctx_keys', set())
129
+ # HACK: `'_connection_info' in self.__dict__` is used as a proxy for a type-check to avoid a
130
+ # cyclic import.
131
+ if licensed or '_connection_info' in self.__dict__:
132
+ if '_connection_info' in self.__dict__ and self._connection_info['no_ctx']:
133
+ object.__setattr__(self, 'ctx', QContext(proxy(self), '', None, no_ctx=True))
134
+ else:
135
+ object.__setattr__(self, 'ctx', QContext(proxy(self), '', None))
136
+ object.__setattr__(self, '_q_ctx_keys', {
137
+ *self.ctx.q._context_keys,
138
+ *self.reserved_words,
139
+ })
140
+ object.__setattr__(self, 'console', QConsole(proxy(self)))
141
+ object.__setattr__(self, 'insert', Insert(proxy(self)))
142
+ object.__setattr__(self, 'upsert', Upsert(proxy(self)))
143
+ object.__setattr__(self, 'query', None) # placeholder
144
+ object.__setattr__(self, 'qsql', QSQL(proxy(self)))
145
+ object.__setattr__(self, 'sql', SQL(proxy(self)))
146
+ object.__setattr__(self, 'read', QReader(proxy(self)))
147
+ object.__setattr__(self, 'write', QWriter(proxy(self)))
148
+ object.__setattr__(self, 'system', SystemCommands(proxy(self)))
149
+
150
+ @abstractmethod
151
+ def __call__(self, query: str, *args, wait: Optional[bool] = None):
152
+ pass # nocov
153
+
154
+ def __getattr__(self, key):
155
+ if key == "__objclass__":
156
+ raise AttributeError
157
+ # Elevate the q context to the global context, as is done in q normally
158
+ try:
159
+ ctx = self.__getattribute__('ctx')
160
+ except BaseException:
161
+ raise exceptions.QError('Cannot load requested context object in unlicensed mode')
162
+ if key in self.__getattribute__('_q_ctx_keys'):
163
+ # if-statement used instead of try-block for performance
164
+ return ctx.q.__getattr__(key)
165
+ try:
166
+ return ctx.__getattr__(key)
167
+ except AttributeError as attribute_error:
168
+ try:
169
+ self.__getattribute__('_register')(name=key)
170
+ ctx._invalidate_cache()
171
+ return ctx.__getattr__(key)
172
+ except Exception as inner_error:
173
+ raise attribute_error from inner_error
174
+
175
+ # __setattr__ takes precedence over data descriptors, so we implement a custom one that uses
176
+ # the default behavior for select keys
177
+ def __setattr__(self, key, value):
178
+ if key in self.__dict__ or key in Q.__dict__:
179
+ object.__setattr__(self, key, value)
180
+ else:
181
+ if key in self.reserved_words or key in self.q:
182
+ raise exceptions.PyKXException(
183
+ 'Cannot assign to reserved word or overwrite q namespace.'
184
+ )
185
+ self._call('set', f'.{key}', value, wait=True)
186
+
187
+ def __delattr__(self, key):
188
+ self.ctx.__delattr__(key)
189
+
190
+ def __getitem__(self, key):
191
+ return self._call('get', key, wait=True)
192
+
193
+ def __setitem__(self, key, value):
194
+ if key in self.reserved_words or key in self.q:
195
+ raise exceptions.PyKXException(
196
+ 'Cannot assign to reserved word or overwrite q namespace.'
197
+ )
198
+ self._call('set', key, value, wait=True)
199
+
200
+ def __delitem__(self, key):
201
+ key = str(key)
202
+ if key.startswith('.'):
203
+ raise exceptions.PyKXException('Cannot delete from the global context.')
204
+ elif key not in self._call('key `.', wait=True).py():
205
+ raise KeyError(key)
206
+ self._call('{![`.;();0b;enlist x]}', key, wait=True) # delete key from `.
207
+
208
+ def __dir__(self):
209
+ if hasattr(self, 'ctx'): # Temporary workaround to disable the context interface over IPC.
210
+ return sorted({
211
+ *super().__dir__(),
212
+ *dir(self.ctx),
213
+ *dir(self.ctx.__getattr__('q')),
214
+ })
215
+ else:
216
+ return super().__dir__()
217
+
218
+ def _register(self,
219
+ name: Optional[str] = None,
220
+ path: Optional[Union[Path, str]] = None,
221
+ ) -> str:
222
+ """Switch to a named q context (read [Q for Mortals
223
+ Chapter 12](https://code.kx.com/q4m3/12_Workspace_Organization/#122-contexts))
224
+ and load variable definitions into that context from a q/k script. Once the script is
225
+ loaded this function switches back to the previous q context.
226
+
227
+ Parameters:
228
+ name: Name to assign the context being loaded. If no argument is provided the
229
+ assigned name of the context is set to the name of the file without filetype
230
+ extension.
231
+ path: Path to the script to load. If no argument is provided this function
232
+ searches for a file matching the given name, loading it if found.
233
+
234
+ Returns:
235
+ The attribute name for the newly loaded module.
236
+ """
237
+ if name is None and path is None:
238
+ raise ValueError('Module name or path must be provided.')
239
+ if path is not None:
240
+ path = Path(path).resolve(strict=True)
241
+ else:
242
+ path = _first_resolved_path([''.join(x) for x in it.product(
243
+ # `str(Path/@)[:-1]` gets the path with a trailing path separator
244
+ (str(x/Path('@'))[:-1] for x in self.paths),
245
+ ('.', ''),
246
+ (name,),
247
+ ('.q', '.k'),
248
+ ('', '_')
249
+ )])
250
+ if " " in str(path) and not suppress_warnings:
251
+ warn("""Space detected in path being loaded.
252
+ KDB-X Python will change directory to the path parent and then load directly before returning to current working directory.
253
+ To turn off this warning set PYKX_SUPPRESS_WARNINGS to True.""", exceptions.PyKXWarning) # noqa: E501
254
+ if name is None: # defaults to filename at end of path sans extension
255
+ name = path.stem
256
+ prev_ctx = self._call('string system"d"', wait=True)
257
+ try:
258
+ self._call('''{[name;folder;file]
259
+ name set (enlist`)!enlist(::);
260
+ system "d ",string name;
261
+ $[@[{get x;1b};`.pykx.util.loadfile;{0b}];
262
+ .pykx.util.loadfile[folder;file];
263
+ system"l ",$[.z.o like "w*";"\\\\";"/"] sv ((),folder;(),file)]}
264
+ ''', "" if name[0] == "." else "."+name, str(path.parent).encode(),
265
+ path.name.encode(), wait=True,
266
+ )
267
+ return name[1:] if name[0] == '.' else name
268
+ finally:
269
+ self._call('{system"d ",x}', prev_ctx, wait=True)
270
+
271
+ @property
272
+ def paths(self):
273
+ """List of locations for the context interface to find q scripts in.
274
+
275
+ Defaults to the current working directory and `#!bash $QHOME`.
276
+
277
+ If you change directories, the current working directory stored in this list
278
+ automatically reflects that change.
279
+ """
280
+ return object.__getattribute__(self, '_paths')
281
+
282
+ @paths.setter
283
+ def paths(self, paths: List[Union[str, Path]]):
284
+ resolved = []
285
+ for path in paths:
286
+ if not isinstance(path, Path):
287
+ path = Path(path)
288
+ try:
289
+ resolved.append(path.resolve(strict=True))
290
+ except FileNotFoundError:
291
+ warn(f"Module path '{path!r}' not found", RuntimeWarning)
292
+ object.__setattr__(self, '_paths', resolved)
293
+
294
+
295
+ # Import order matters here, so the imports are not ordered conventionally.
296
+ from .serialize import deserialize, serialize
297
+ from .console import QConsole
298
+ from .ctx import default_paths, QContext
299
+ from .query import Insert, QSQL, SQL, Upsert
300
+ from .read import QReader
301
+ from .system import SystemCommands
302
+ from .write import QWriter
303
+ from .reimporter import PyKXReimport
304
+ from .module import QModule, QNamespaceProxy, use
305
+
306
+ from . import config
307
+ from . import console
308
+ from . import exceptions
309
+ from . import wrappers
310
+ from . import schema
311
+ from . import streamlit
312
+ from . import random
313
+ from . import help
314
+
315
+ from ._wrappers import _init as _wrappers_init
316
+ _wrappers_init(wrappers)
317
+
318
+ from .embedded_q import EmbeddedQ, EmbeddedQFuture, q
319
+ from ._version import version as __version__
320
+ from .exceptions import *
321
+
322
+ from .util import _init as _util_init
323
+ _util_init(q)
324
+
325
+ from ._ipc import _init as _ipc_init
326
+ _ipc_init(q)
327
+
328
+ from .compress_encrypt import Compress, CompressionAlgorithm, Encrypt
329
+ from .db import DB
330
+ from .tick import TICK
331
+ from .ipc import AsyncQConnection, QConnection, QFuture, RawQConnection, SecureQConnection, SyncQConnection # noqa
332
+ from .config import qargs, qhome, qlic, suppress_warnings
333
+ from .wrappers import *
334
+ from .wrappers import CharVector, K
335
+
336
+ from ._ipc import ssl_info
337
+
338
+ from .schema import _init as _schema_init
339
+ _schema_init(q)
340
+
341
+ from .register import _init as _register_init
342
+ _register_init(q)
343
+
344
+ from .license import _init as _license_init
345
+ _license_init(q)
346
+
347
+ from .random import _init as _random_init
348
+ _random_init(q)
349
+
350
+ from .db import _init as _db_init
351
+ _db_init(q)
352
+
353
+ from .tick import _init as _tick_init
354
+ _tick_init(q)
355
+
356
+ from .remote import _init as _remote_init
357
+ _remote_init(q)
358
+
359
+ from .compress_encrypt import _init as _compress_init
360
+ _compress_init(q)
361
+
362
+ from .help import _init as _help_init
363
+ _help_init(q)
364
+ qhelp = help.qhelp
365
+
366
+ from .module import _init as _module_init
367
+ _module_init(q)
368
+
369
+ if k_allocator:
370
+ from . import _numpy as _pykx_numpy_cext
371
+
372
+
373
+ def merge_asof(left, *args, **kwargs):
374
+ return left.merge_asof(*args, **kwargs)
375
+
376
+
377
+ if sys.version_info[1] < 9:
378
+ warn(
379
+ 'Python 3.9 is the minimum support version. Please upgrade.',
380
+ exceptions.PyKXWarning
381
+ )
382
+
383
+
384
+ def install_into_QHOME(overwrite_embedpy=False,
385
+ to_local_folder=False,
386
+ cloud_libraries=False) -> None:
387
+ """Copies the embedded Python functionality of KDB-X Python into `$QHOME`.
388
+
389
+ Parameters:
390
+ overwrite_embedpy: If embedPy had previously been installed replace it otherwise
391
+ save functionality as pykx.q.
392
+ to_local_folder: Copy the files to your local folder using True, or specify '/some/path'
393
+ rather than `#!bash QHOME`.
394
+ cloud_libraries: Copy cloud libraries to `#!bash QHOME`.
395
+
396
+ Returns:
397
+ None
398
+ """
399
+ if isinstance(to_local_folder, (str, Path)):
400
+ dest = Path(to_local_folder)
401
+ elif to_local_folder:
402
+ dest = Path('.')
403
+ else:
404
+ dest = qhome
405
+ if dest == pykx_lib_dir:
406
+ warn(f"""
407
+ The destination {dest} matches pykx.config.pykx_lib_dir which already contains pykx.q,
408
+ no files will be copied.
409
+ If your environment does not have QHOME set or you wish to control where pykx.q
410
+ is installed use to_local_folder:
411
+ to_local_folder = True will install pykx.q to your current working directory
412
+ to_local_folder = 'path/to/folder' will install pykx.q to the specified path
413
+ """)
414
+ return
415
+
416
+ p = Path(dest)/'p.k'
417
+ c_files = ['kurl.q_', 'kurl.sidecar.q_', 'objstor.q_', 'qlog.q_', 'rest.q_', 'bq.q_']
418
+
419
+ if not p.exists() or overwrite_embedpy:
420
+ shutil.copy(Path(__file__).parent/'p.k', p)
421
+ if overwrite_embedpy:
422
+ shutil.copy(Path(__file__).parent/'p.k', Path(dest)/'p.q')
423
+ if cloud_libraries:
424
+ for i in c_files:
425
+ shutil.copy(Path(__file__).parent/'lib'/i, Path(dest)/i)
426
+ shutil.copy(Path(__file__).parent/'lib'/'sql.k_', Path(dest)/'s.k_')
427
+ shutil.copy(Path(__file__).parent/'pykx.q', dest)
428
+ shutil.copy(Path(__file__).parent/'pykx_init.q_', dest)
429
+
430
+
431
+ def activate_numpy_allocator() -> None:
432
+ """Sets the allocator used for Numpy array data to one optimized for use with KDB-X Python.
433
+
434
+ This will only change the default allocator if the environment variable `PYKX_NO_ALLOCATOR`
435
+ is not set to 1 or if the flag `--pykxnoalloc` is not present in the QARGS environment variable.
436
+
437
+ The name of the allocator set by this function is `'pykx_allocator'`.
438
+
439
+ The name of the active Numpy allocator can be retrieved by running
440
+ `numpy.core.multiarray.get_handler_name()`.
441
+
442
+ Refer to NEP 49 for details about custom Numpy allocators: https://numpy.org/neps/nep-0049.html
443
+
444
+ The custom allocator set by KDB-X Python allocates Numpy array data into the embedded q memory
445
+ space.
446
+ Numpy arrays created with this allocator can be converted into a q vector without copying the
447
+ data.
448
+
449
+ Because q objects must have their metadata immediately preceding the data, only a single
450
+ q vector can be created using this approach. Repeated conversions of the Numpy array into a q
451
+ vector will yield the same q vector with its reference count incremented by 1 each time.
452
+
453
+ Since Numpy stores its metadata separately from its data, multiple views can be taken over the
454
+ data without incurring a copy. The same is not true for q vectors. If a Numpy array is
455
+ converted into a q vector, then a view is taken of the Numpy array, this results in the view
456
+ being converted into a q vector and a copy of the data being incurred. Regardless of whether
457
+ this custom Numpy allocator is activated.
458
+
459
+ The Numpy array data object holds a reference to the q vector. When the Numpy array data object
460
+ is deallocated, it decrements the refcount of the q vector.
461
+
462
+ If the installed version of Numpy support custom allocators (i.e. is greater than or equal to
463
+ version `1.22.0`), and the active Numpy allocator when KDB-X Python is imported is the default
464
+ allocator, KDB-X Python will automatically call this function.
465
+
466
+ Note that Numpy arrays created with an allocator other than `'pykx_allocator'` (e.g. before
467
+ KDB-X Python has been imported) will not gain the benefits enabled by this allocator.
468
+ The name of the allocator used by a given array `x` can be retrieved by running
469
+ `numpy.core.multiarray.get_handler_name(x)`.
470
+ """
471
+ if k_allocator:
472
+ _pykx_numpy_cext.activate_pykx_allocators()
473
+
474
+
475
+ import numpy as np
476
+ if k_allocator:
477
+ _pykx_numpy_cext.init_numpy_ctx(core._r0_ptr, core._k_ptr, core._ktn_ptr)
478
+ if np.__version__[0] == '1':
479
+ handler = np.core.multiarray.get_handler_name()
480
+ else:
481
+ handler = np._core.multiarray.get_handler_name()
482
+ if handler == 'default_allocator':
483
+ activate_numpy_allocator()
484
+
485
+
486
+ def deactivate_numpy_allocator():
487
+ """Deactivate the KDB-X Python Numpy allocator.
488
+
489
+ If the KDB-X Python Numpy allocator has not been activated then this function has no effect.
490
+ Otherwise, it replaces the KDB-X Python Numpy allocator with the allocator that was in use
491
+ when it was first activated.
492
+ """
493
+ if k_allocator:
494
+ _pykx_numpy_cext.deactivate_pykx_allocators()
495
+
496
+
497
+ try:
498
+ # If we are running under IPython/Jupyter...
499
+ ipython = get_ipython() # noqa
500
+ # Load the KDB-X Python extension for Jupyter Notebook.
501
+ ipython.extension_manager.load_extension('pykx.nbextension')
502
+
503
+ if config.jupyterq:
504
+ util.jupyter_qfirst_enable()
505
+ except NameError:
506
+ # Not running under IPython/Jupyter...
507
+ pass
508
+
509
+ shutdown_thread = core.shutdown_thread
510
+
511
+ if licensed:
512
+ days_to_expiry = q('"D"$', q.z.l[1]) - q.z.D
513
+ if days_to_expiry < 10:
514
+ logging.warning(f'KDB-X Python license set to expire in {int(days_to_expiry)} days, '
515
+ 'please consider installing an updated license')
516
+
517
+ __all__ = sorted([
518
+ 'AsyncQConnection',
519
+ 'deserialize',
520
+ 'EmbeddedQ',
521
+ 'EmbeddedQFuture',
522
+ 'Q',
523
+ 'qargs',
524
+ 'QConnection',
525
+ 'QConsole',
526
+ 'QContext',
527
+ 'QFuture',
528
+ 'qhome',
529
+ 'QReader',
530
+ 'random',
531
+ 'remote',
532
+ 'QWriter',
533
+ 'qlic',
534
+ 'serialize',
535
+ 'SyncQConnection',
536
+ 'RawQConnection',
537
+ 'activate_numpy_allocator',
538
+ 'console',
539
+ 'ctx',
540
+ 'deactivate_numpy_allocator',
541
+ 'exceptions',
542
+ 'install_into_QHOME',
543
+ 'licensed',
544
+ 'toq',
545
+ 'schema',
546
+ 'config',
547
+ 'util',
548
+ 'q',
549
+ 'QModule',
550
+ 'QNamespaceProxy',
551
+ 'use',
552
+ 'shutdown_thread',
553
+ 'PyKXReimport',
554
+ 'help',
555
+ 'qhelp',
556
+ *exceptions.__all__,
557
+ *wrappers.__all__,
558
+ ])
559
+
560
+ if not no_pykx_signal:
561
+ for k, v in _signal_dict.items():
562
+ try:
563
+ signal.signal(eval(k), v)
564
+ except Exception:
565
+ pass
566
+
567
+
568
+ def __dir__():
569
+ return __all__
570
+
571
+
572
+ envlist = (
573
+ 'PYKX_UNDER_Q',
574
+ 'PYKX_UNDER_PYTHON',
575
+ 'PYKX_Q_LOADED_MARKER',
576
+ 'PYKX_LOADED_UNDER_Q',
577
+ 'PYKX_LOADING_PYTHON',
578
+ 'PYKX_LOADING_Q',
579
+ 'PYKX_QINIT_CHECK',
580
+ )
581
+ if os.getenv('PYKX_LOADING_PYTHON', '') != '':
582
+ for x in envlist:
583
+ os.environ[x] = ''
584
+ del os.environ[x]
585
+ if licensed:
586
+ q(f'setenv[`{x}; ""]')