Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@

The changelog presented here outlines changes to PyKX when operating within a Python environment specifically, if you require changelogs associated with PyKX operating under a q environment see [here](./underq-changelog.md).

## PyKX 3.1.4

#### Release Date

2025-07-17

### Fixes and Improvements

- Resolved error on import when `PYKX_THREADING` was set.
- Fixed an issue where data could be corrupted in keyed columns of PyArrow backed Pandas dataframes.

## PyKX 3.1.3

#### Release Date
Expand Down
31 changes: 31 additions & 0 deletions docs/release-notes/underq-changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,37 @@ This changelog provides updates from PyKX 2.0.0 and above, for information relat

The changelog presented here outlines changes to PyKX when operating within a q environment specifically, if you require changelogs associated with PyKX operating within a Python environment see [here](./changelog.md).

## PyKX 3.1.4

#### Release Date

2025-07-17

### Fixes and Improvements

- Resolved `object has no attribute 't'` error for certain conversions

=== "Behaviour prior to change"

```python
q).pykx.setdefault (),"k";
q).pykx.import[`numpy.random][`:choice][01b; 10]
'AttributeError("'numpy.int64' object has no attribute 't'")
[2] /home/user/q/pykx.q:241: .pykx.util.pykx:
];
wrap pyfunc[f] . x];
^
":"~first a0:string x0;
```

=== "Behaviour post change"

```python
q).pykx.setdefault (),"k";
q).pykx.import[`numpy.random][`:choice][01b; 10]
1011111110b
```

## PyKX 3.1.3

#### Release Date
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/advanced/ipc.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ In the below examples you can connect to a process on port 5050 and run a query.

```python
>>> conn = await kx.AsyncQConnection('localhost', 5050)
>>> print(await conn('til 10').py())
>>> print((await conn('til 10')).py())
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> conn.close()
```
Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/advanced/numpy.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ _This page explains how to integrate PyKX with NumPy._

PyKX is designed for advanced integration with NumPy. This integration is built on three pillars:

- [NEP-49](https://numpy.org/neps/nep-0049.html)
- [NEP-49](https://numpy.org/neps/nep-0049-data-allocation-strategies.html)
- the NumPy [array interface](https://numpy.org/doc/stable/reference/arrays.interface.html)
- [universal functions](https://numpy.org/doc/stable/reference/ufuncs.html)

Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ test = [
"pytest-randomly==3.11.0",
"pytest-xdist==2.5.0",
"pytest-order==1.1.0",
"pytest-rerunfailures==10.3",
"psutil>=5.0.0",
"pytest-timeout>=2.0.0",
"IPython",
Expand Down
2 changes: 1 addition & 1 deletion src/pykx/core.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def _link_qhome():
warn('Unable to connect user QHOME to PyKX QHOME via symlinks.\n' # nocov
'To permanently disable attempts to create symlinks you can\n' # nocov
'\t1. Set the environment variable "PYKX_IGNORE_QHOME" = True.\n' # nocov
'\t2. Update the file ".pykx.config" using kx.util.add_to_config({\'PYKX_IGNORE_QHOME\': \'True\'})\n' # nocov
'\t2. Update the file ".pykx-config" using kx.util.add_to_config({\'PYKX_IGNORE_QHOME\': \'True\'})\n' # nocov
f'Error: {ex}\n', # nocov
PyKXWarning) # nocov
return # nocov
Expand Down
2 changes: 1 addition & 1 deletion src/pykx/embedded_q.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def __call__(self,
query: The code to run in the q instance.
*args: Arguments to the q query. Each argument is converted into a
`#!python pykx.K` object. This parameter supports up to 8 args (the maximum amount
supported by q functions), providing more causesw an error.
supported by q functions), providing more causes an error.
wait: A keyword to allow users to call any `#!python pykx.EmbeddedQ` or
`#!python pykx.QConnection` instance the same way. All queries executed by this
function are synchronous on the embedded q instance. Using a `#!python False`
Expand Down
2 changes: 1 addition & 1 deletion src/pykx/nbextension.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def write_to_q_file(_q, locked, path, code):
if locked:
output_file = Path(path[:-1])
_q('0:', output_file, [kx.CharVector(code)])
_q('\_ ' + path[:-1])
_q('\\_ ' + path[:-1])
_q('hdel', output_file)
else:
output_file = Path(path)
Expand Down
20 changes: 10 additions & 10 deletions src/pykx/pykx.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ static PyObject* k_to_py_list(K x) {
EXPORT K k_to_py_foreign(K x, K typenum, K israw) {
K k;
if (pykx_threading)
return raise_k_error("pykx.q is not supported when using PYKX_THREADING");
return raise_k_error("pyForeign is not supported when using PYKX_THREADING");
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
PyObject* p = k_to_py_cast(x, typenum, israw);
Expand Down Expand Up @@ -291,7 +291,7 @@ void construct_args_kwargs(PyObject* params, PyObject** args, PyObject** kwargs,
EXPORT K k_pyrun(K k_ret, K k_eval_or_exec, K as_foreign, K k_code_string) {

if (pykx_threading)
return raise_k_error("pykx.q is not supported when using PYKX_THREADING");
return raise_k_error("pyrun is not supported when using PYKX_THREADING");
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
K k;
Expand Down Expand Up @@ -435,7 +435,7 @@ EXPORT K k_modpow(K k_base, K k_exp, K k_mod_arg) {

EXPORT K foreign_to_q(K f, K b) {
if (pykx_threading)
return raise_k_error("pykx.q is not supported when using PYKX_THREADING");
return raise_k_error("foreignToq is not supported when using PYKX_THREADING");
if (f->t != 112)
return raise_k_error("Expected foreign object for call to .pykx.toq");
if (!check_py_foreign(f))
Expand Down Expand Up @@ -483,7 +483,7 @@ EXPORT K foreign_to_q(K f, K b) {

EXPORT K repr(K as_repr, K f) {
if (pykx_threading)
return raise_k_error("pykx.q is not supported when using PYKX_THREADING");
return raise_k_error("repr is not supported when using PYKX_THREADING");
K k;
if (f->t != 112) {
if (as_repr->g){
Expand Down Expand Up @@ -526,7 +526,7 @@ EXPORT K repr(K as_repr, K f) {

EXPORT K get_attr(K f, K attr) {
if (pykx_threading)
return raise_k_error("pykx.q is not supported when using PYKX_THREADING");
return raise_k_error("getattr is not supported when using PYKX_THREADING");
K k;
if (f->t != 112) {
if (f->t == 105) {
Expand Down Expand Up @@ -555,7 +555,7 @@ EXPORT K get_attr(K f, K attr) {

EXPORT K get_global(K attr) {
if (pykx_threading)
return raise_k_error("pykx.q is not supported when using PYKX_THREADING");
return raise_k_error("getGlobal is not supported when using PYKX_THREADING");
K k;
if (attr->t != -11) {
return raise_k_error("Expected a SymbolAtom for the attribute to get in .pykx.get");
Expand All @@ -582,7 +582,7 @@ EXPORT K get_global(K attr) {

EXPORT K set_global(K attr, K val) {
if (pykx_threading)
return raise_k_error("pykx.q is not supported when using PYKX_THREADING");
return raise_k_error("setGlobal is not supported when using PYKX_THREADING");
K k;
int gstate = PyGILState_Ensure();

Expand All @@ -608,7 +608,7 @@ EXPORT K set_global(K attr, K val) {

EXPORT K set_attr(K f, K attr, K val) {
if (pykx_threading)
return raise_k_error("pykx.q is not supported when using PYKX_THREADING");
return raise_k_error("setattr is not supported when using PYKX_THREADING");
if (f->t != 112) {
if (f->t == 105) {
return raise_k_error("Expected foreign object for call to .pykx.setattr, try unwrapping the foreign object with `.");
Expand Down Expand Up @@ -638,7 +638,7 @@ EXPORT K set_attr(K f, K attr, K val) {

EXPORT K import(K module) {
if (pykx_threading)
return raise_k_error("pykx.q is not supported when using PYKX_THREADING");
return raise_k_error("import is not supported when using PYKX_THREADING");
K k;
K res;
if (module->t != -11)
Expand All @@ -660,7 +660,7 @@ EXPORT K import(K module) {
EXPORT K call_func(K f, K has_no_args, K args, K kwargs) {

if (pykx_threading)
return raise_k_error("pykx.q is not supported when using PYKX_THREADING");
return raise_k_error("import is not supported when using PYKX_THREADING");
K k;
PyObject* pyf = NULL;

Expand Down
Binary file removed src/pykx/pykx_init.q_
Binary file not shown.
Binary file modified src/pykx/q.so/qk/pykx_init.q_
Binary file not shown.
2 changes: 1 addition & 1 deletion src/pykx/toq.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1782,7 +1782,7 @@ def from_pandas_index(x: pd.Index,
index_dict = from_dict(d)
return factory(<uintptr_t>core.xT(core.r1(_k(index_dict))), False)
elif isinstance(x, _supported_pandas_index_types_via_numpy):
return from_numpy_ndarray(x.to_numpy(), cast=cast, handle_nulls=handle_nulls)
return from_numpy_ndarray(x.to_numpy().copy(), cast=cast, handle_nulls=handle_nulls)
else:
raise _conversion_TypeError(x, 'Pandas index', ktype)

Expand Down
2 changes: 1 addition & 1 deletion src/pykx/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
**kwargs
)

if res.t >= 0:
if isinstance(res, K) and res.t >= 0:
res = res._unlicensed_getitem(0)
return res

Expand Down
10 changes: 0 additions & 10 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,6 @@

os.environ['PYTHONWARNINGS'] = 'ignore:No data was collected,ignore:Module pykx was never imported'


# Addition of configuration toml used in testing
# The configuration values set here are the default values for the PyKX so should not
# overwrite test behavior
config_file = open(Path.home()/".pykx.config", "w")
config_content = {"default": {"PYKX_KEEP_LOCAL_TIMES", 0}}
toml.dump(config_content, config_file)
config_file.close()


if system() != 'Windows':
if threading.current_thread() == threading.main_thread():
signal.signal(signal.SIGUSR1, lambda *_: None)
Expand Down
7 changes: 7 additions & 0 deletions tests/qcumber_tests/import.quke
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@ feature .pykx.import
@[{.pykx.import x; 0b};
"a";
{x like "Module to be imported must be a symbol"}]

should allow generation of random objects
expect generation without errors
def:.pykx.setdefault (),"k";
imp:.pykx.import[`numpy.random][`:choice][01b; 10]`;
10 = count imp

24 changes: 24 additions & 0 deletions tests/test_cloud_edition.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ def test_objstor_aws_read1(q, kx):
assert isinstance(res, kx.ByteVector)


@pytest.mark.skipif(
os.getenv('PYKX_THREADING') is not None,
reason='ToDo investigate in KXI-63220'
)
def test_qlog_fd_stdout_endpoint(q):
"""Test STDOUT endpoint creation."""
q('.com_kx_log.fd.i.write:{.fd.cache,:enlist(x;y)}')
Expand All @@ -274,6 +278,10 @@ def test_qlog_fd_stdout_endpoint(q):
q('.com_kx_log.lcloseAll[]')


@pytest.mark.skipif(
os.getenv('PYKX_THREADING') is not None,
reason='ToDo investigate in KXI-63220'
)
def test_qlog_fd_stderr_setup(q):
"""Tests creation of stderr endpoint."""
q('id:.com_kx_log.lopen[`:fd://stderr]')
Expand All @@ -282,6 +290,10 @@ def test_qlog_fd_stderr_setup(q):
q('.com_kx_log.lcloseAll[]')


@pytest.mark.skipif(
os.getenv('PYKX_THREADING') is not None,
reason='ToDo investigate in KXI-63220'
)
def test_qlog_fd_stdout_log_string(q):
"""Test default string logging."""
q('.com_kx_log.fd.i.write:{.fd.cache,:enlist(x;y)}')
Expand All @@ -300,6 +312,10 @@ def test_qlog_fd_stdout_log_string(q):
q('.com_kx_log.lcloseAll[]')


@pytest.mark.skipif(
os.getenv('PYKX_THREADING') is not None,
reason='ToDo investigate in KXI-63220'
)
def test_qlog_fd_stdout_custom_log(q):
"""Tests custom string logging."""
q('.com_kx_log.fd.i.write:{.fd.cache,:enlist(x;y)}')
Expand All @@ -316,6 +332,10 @@ def test_qlog_fd_stdout_custom_log(q):
q('.com_kx_log.lcloseAll[]')


@pytest.mark.skipif(
os.getenv('PYKX_THREADING') is not None,
reason='ToDo investigate in KXI-63220'
)
def test_qlog_fd_stdout_dict_log(q):
"""Tests dictionary message logging."""
q('.com_kx_log.fd.i.write:{.fd.cache,:enlist(x;y)}')
Expand All @@ -330,6 +350,10 @@ def test_qlog_fd_stdout_dict_log(q):
q('.com_kx_log.lcloseAll[]')


@pytest.mark.skipif(
os.getenv('PYKX_THREADING') is not None,
reason='ToDo investigate in KXI-63220'
)
def test_qlog_fd_file_publish(q):
"""Tests logging message to file."""
q('.com_kx_log.fd.i.write:{.fd.cache,:enlist(x;y)}')
Expand Down
5 changes: 5 additions & 0 deletions tests/test_sql.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Do not import pykx here - use the `kx` fixture instead!
import os
from abc import ABCMeta

import pytest
Expand Down Expand Up @@ -163,6 +164,10 @@ def test_sql_get_input_values(q, kx):
assert q.sql.get_input_types(p2) == ['SymbolAtom/SymbolVector', 'FloatAtom/FloatVector']


@pytest.mark.skipif(
os.getenv('PYKX_THREADING') is not None,
reason='ToDo investigate in KXI-63220'
)
@pytest.mark.embedded
def test_sql_string_col(q):
q('t:([] optid:1 2 3;Market:`a`b`CBOE;date:3#2023.11.14;Symbol:("a";"b";"odMP=20"))')
Expand Down
16 changes: 8 additions & 8 deletions tests/win_tests.bat
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@ python .\parse_tests.py

cd ..

python -m pytest -vvv -n 0 --no-cov --junitxml=licensed_report.xml .\tests\win_tests\lic\licensed_tests.py
python -m pytest -vvv -n 0 --no-cov --color=yes --reruns=5 --junitxml=licensed_report.xml .\tests\win_tests\lic\licensed_tests.py
SET /A licensed = %ERRORLEVEL%
python -m pytest -vvv -n 0 --no-cov --junitxml=unlicensed_report.xml .\tests\win_tests\unlic\unlicensed_tests.py
python -m pytest -vvv -n 0 --no-cov --color=yes --reruns=5 --junitxml=unlicensed_report.xml .\tests\win_tests\unlic\unlicensed_tests.py
SET /A unlicensed = %ERRORLEVEL%
python -m pytest -vvv -n 0 --no-cov --junitxml=ipc_licensed_report.xml .\tests\win_tests\ipc_lic\ipc_licensed_tests.py
python -m pytest -vvv -n 0 --no-cov --color=yes --reruns=5 --junitxml=ipc_licensed_report.xml .\tests\win_tests\ipc_lic\ipc_licensed_tests.py
SET /A ipc_licensed = %ERRORLEVEL%
python -m pytest -vvv -n 0 --no-cov --junitxml=ipc_unlicensed_report.xml .\tests\win_tests\ipc_unlic\ipc_unlicensed_tests.py
python -m pytest -vvv -n 0 --no-cov --color=yes --reruns=5 --junitxml=ipc_unlicensed_report.xml .\tests\win_tests\ipc_unlic\ipc_unlicensed_tests.py
SET /A ipc_unlicensed = %ERRORLEVEL%
python -m pytest -vvv -n 0 --no-cov --junitxml=embedded_report.xml .\tests\win_tests\embedded\embedded_tests.py
python -m pytest -vvv -n 0 --no-cov --color=yes --reruns=5 --junitxml=embedded_report.xml .\tests\win_tests\embedded\embedded_tests.py
SET /A embedded = %ERRORLEVEL%
python -m pytest -vvv -n 0 --no-cov --junitxml=nep_licensed_report.xml .\tests\win_tests\nep_lic\nep_licensed_tests.py
python -m pytest -vvv -n 0 --no-cov --color=yes --reruns=5 --junitxml=nep_licensed_report.xml .\tests\win_tests\nep_lic\nep_licensed_tests.py
SET /A nep_licensed = %ERRORLEVEL%
python -m pytest -vvv -n 0 --no-cov --junitxml=nep_unlicensed_report.xml .\tests\win_tests\nep_unlic\nep_unlicensed_tests.py
python -m pytest -vvv -n 0 --no-cov --color=yes --reruns=5 --junitxml=nep_unlicensed_report.xml .\tests\win_tests\nep_unlic\nep_unlicensed_tests.py
SET /A nep_unlicensed = %ERRORLEVEL%
python -m pytest -vvv -n 0 --no-cov --junitxml=pandas_licensed_report.xml .\tests\win_tests\pandas_lic\pandas_licensed_tests.py
python -m pytest -vvv -n 0 --no-cov --color=yes --reruns=5 --junitxml=pandas_licensed_report.xml .\tests\win_tests\pandas_lic\pandas_licensed_tests.py
SET /A pandas_licensed = %ERRORLEVEL%
IF %licensed% NEQ 0 (
exit %licensed%
Expand Down