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
15 changes: 13 additions & 2 deletions docs/release-notes/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@

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.7
# PyKX 3.1.8

#### Release Date

2026-02-09
2026-02-18

### Additions

- Added `connection_timeout` keyword argument to `AsyncQConnection` objects allowing the specification of a timeout for the initial connection to a `q` server.
- Added the usage of `.q.Q.ens` for `sym_enum` argument in `DB` objects.

## PyKX 3.1.7

#### Release Date

2026-02-10

### Additions

Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ nav:
- PyKX as a server: examples/server/server.md
- Real-time streaming: examples/streaming/index.md
- Multithreaded execution: examples/threaded_execution/threading.md
- Asyncronous Queries: examples/AsynchronousQueries/async_querying.md
- Asynchronous Queries: examples/AsynchronousQueries/async_querying.md
- Releases:
- Release notes:
- PyKX: release-notes/changelog.md
Expand Down
11 changes: 10 additions & 1 deletion src/pykx/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,13 @@ def __init__(self,
>>> db.tables
['quote', 'trade']
```
!!! note "Loading root-level objects"
When using `pykx.DB`, only partitioned tables are exposed as attributes of the
`DB` object. Root-level splayed objects (for example, dictionaries or lists stored
alongside partitions) are **not** attached to the `DB` instance, but are still loaded
into the q process and can be accessed directly using `kx.q`.

For example, you can retrieve a variable `mySplayTab` by running `kx.q['mySplayTab']`.
Define the path to be used for a database which does not initially exist

```python
Expand Down Expand Up @@ -323,7 +329,10 @@ def create(self,
qfunc = q(_func_mapping[func_name])
try:
if format == 'splayed':
table = q.Q.en(save_dir, table)
if sym_enum is None:
table = q.Q.en(save_dir, table)
else:
table = q.Q.ens(save_dir, table, sym_enum)
q('{.Q.dd[x;`] set y}', save_dir/table_name, table)
else:
if isinstance(partition, str):
Expand Down
57 changes: 48 additions & 9 deletions src/pykx/ipc.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,9 @@ def __init__(self,
no_ctx: bool = False,
reconnection_attempts: int = -1,
reconnection_delay: float = 0.5,
reconnection_function: callable = reconnection_function
reconnection_function: callable = reconnection_function,
connection_timeout: Optional[float] = None,

):
"""Interface with a q process using the q IPC protocol.

Expand Down Expand Up @@ -498,6 +500,9 @@ def __init__(self,
reconnection_delay: A `#!python float` for the initial delay between reconnect attempts
(in seconds). This is passed to the provided `#!python reconnection_function` that
is executed on reconnect attempt.
connection_timeout: Timeout in seconds for connection to `q` server. If left as default
`None`, the socket will not timeout when connecting.


Note: The `#!python username` and `#!python password` parameters are not required.
The `#!python username` and `#!python password` parameters are only required if the
Expand All @@ -518,6 +523,9 @@ def __init__(self,
super().__init__()

def _create_connection_to_server(self):
con_timeout = self._connection_info['connection_timeout']\
if self._connection_info['connection_timeout'] is not None\
else self._connection_info['timeout']
object.__setattr__(
self,
'_handle',
Expand All @@ -527,7 +535,7 @@ def _create_connection_to_server(self):
self._connection_info['credentials'],
self._connection_info['unix'],
self._connection_info['tls'],
self._connection_info['timeout'],
con_timeout,
self._connection_info['large_messages']
)
)
Expand Down Expand Up @@ -564,7 +572,8 @@ def _init(self,
conn_gc_time: float = 0.0,
reconnection_attempts: int = -1,
reconnection_delay: float = 0.5,
reconnection_function: callable = reconnection_function
reconnection_function: callable = reconnection_function,
connection_timeout: Optional[float] = None,
):
credentials = f'{normalize_to_str(username, "Username")}:' \
f'{normalize_to_str(password, "Password")}'
Expand All @@ -585,7 +594,8 @@ def _init(self,
'conn_gc_time': conn_gc_time,
'reconnection_attempts': reconnection_attempts,
'reconnection_delay': reconnection_delay,
'reconnection_function': reconnection_function
'reconnection_function': reconnection_function,
'connection_timeout': connection_timeout,
})
if system == 'Windows' and unix: # nocov
raise TypeError('Unix domain sockets cannot be used on Windows')
Expand All @@ -600,13 +610,14 @@ def _init(self,
object.__setattr__(self, '_handle', server_sock.fileno())
object.__setattr__(self, '_finalizer', lambda: server_sock.close())
else:
con_timeout = connection_timeout if connection_timeout is not None else timeout
try:
handle = _ipc.init_handle(host,
port,
credentials,
unix,
tls,
timeout,
con_timeout,
large_messages)
except BaseException as e:
if isinstance(e, QError):
Expand Down Expand Up @@ -1140,7 +1151,8 @@ def __init__(self,
no_ctx: bool = False,
reconnection_attempts: int = -1,
reconnection_delay: float = 0.5,
reconnection_function: callable = reconnection_function
reconnection_function: callable = reconnection_function,
connection_timeout: Optional[float] = None,
):
"""Interface with a q process using the q IPC protocol.

Expand Down Expand Up @@ -1181,6 +1193,9 @@ def __init__(self,
`#!python reconnection_delay` on successive attempts to reconnect to the server. By
default this is an exponential backoff where the `#!python reconnection_delay` is
multiplied by two on each invocation.
connection_timeout: Timeout in seconds for connection to `q` server. If left as default
`None`, the socket will not timeout when connecting.


Note: The `#!python username` and `#!python password` parameters are not required.
The `#!python username` and `#!python password` parameters are only required if the
Expand Down Expand Up @@ -1247,7 +1262,9 @@ def __init__(self,
no_ctx=no_ctx,
reconnection_attempts=reconnection_attempts,
reconnection_delay=reconnection_delay,
reconnection_function=reconnection_function
reconnection_function=reconnection_function,
connection_timeout=connection_timeout,

)
super().__init__()

Expand Down Expand Up @@ -1453,7 +1470,9 @@ def __init__(self,
no_ctx: bool = False,
reconnection_attempts: int = -1,
reconnection_delay: float = 0.5,
reconnection_function: callable = reconnection_function
reconnection_function: callable = reconnection_function,
connection_timeout: Optional[float] = None,

):
"""Interface with a q process using the q IPC protocol.

Expand Down Expand Up @@ -1499,6 +1518,9 @@ def __init__(self,
`#!python reconnection_delay` on successive attempts to reconnect to the server. By
default this is an exponential backoff where the `#!python reconnection_delay` is
multiplied by two on each invocation
connection_timeout: Timeout in seconds for connection to `q` server. If left as default
`None`, the socket will not timeout when connecting.


Note: The `#!python username` and `#!python password` parameters are not required.
The `#!python username` and `#!python password` parameters are only required if
Expand Down Expand Up @@ -1593,6 +1615,7 @@ async def main():
'reconnection_attempts':reconnection_attempts,
'reconnection_delay': reconnection_delay,
'reconnection_function': reconnection_function,
'connection_timeout': connection_timeout,
})
object.__setattr__(self, '_initialized', False)

Expand All @@ -1613,6 +1636,7 @@ async def _async_init(self,
reconnection_attempts: int = -1,
reconnection_delay: float = 0.5,
reconnection_function: callable = reconnection_function,
connection_timeout: float = 0.0,
):
object.__setattr__(self, '_call_stack', [])
self._init(host,
Expand All @@ -1630,6 +1654,7 @@ async def _async_init(self,
reconnection_attempts=reconnection_attempts,
reconnection_delay=reconnection_delay,
reconnection_function=reconnection_function,
connection_timeout=connection_timeout,
)
object.__setattr__(self, '_loop', event_loop)
con_info = object.__getattribute__(self, '_connection_info')
Expand Down Expand Up @@ -1657,6 +1682,7 @@ async def _initobj(self): # nocov
reconnection_attempts=self._stored_args['reconnection_attempts'],
reconnection_delay=self._stored_args['reconnection_delay'],
reconnection_function=self._stored_args['reconnection_function'],
connection_timeout=self._stored_args['connection_timeout'],
)
return self

Expand Down Expand Up @@ -2055,6 +2081,7 @@ def __init__(self,
no_ctx: bool = False,
as_server: bool = False,
conn_gc_time: float = 0.0,
connection_timeout: Optional[float] = None,
):
"""Interface with a q process using the q IPC protocol.

Expand Down Expand Up @@ -2093,6 +2120,9 @@ def __init__(self,
going through the list of opened connections and closing any that the clients have
closed. If not set the default of 0.0 will cause any old connections to never be
closed unless `#!python self.clean_open_connections()` is manually called.
connection_timeout: Timeout in seconds for connection to `q` server. If left as default
`None`, the socket will not timeout when connecting.


Note: The `#!python username` and `#!python password` parameters are not required.
The `#!python username` and `#!python password` parameters are only required if the q
Expand Down Expand Up @@ -2137,7 +2167,7 @@ def __init__(self,
```
"""
if timeout > 0.0:
warnings.warn('Timeout is not supported when using AsyncQConnection objects.')
warnings.warn('Timeout is not supported when using RawQConnection objects.')
# TODO: Remove this once TLS support is fixed
if tls:
raise PyKXException('TLS is currently only supported for SyncQConnections')
Expand All @@ -2164,6 +2194,7 @@ def __init__(self,
'no_ctx': True if as_server else no_ctx,
'as_server': as_server,
'conn_gc_time': conn_gc_time,
'connection_timeout': connection_timeout,
})
object.__setattr__(self, '_initialized', False)

Expand All @@ -2182,6 +2213,7 @@ async def _async_init(self,
no_ctx: bool = False,
as_server: bool = False,
conn_gc_time: float = 0.0,
connection_timeout: Optional[float] = None,
):
object.__setattr__(self, '_call_stack', [])
object.__setattr__(self, '_send_stack', [])
Expand All @@ -2198,6 +2230,7 @@ async def _async_init(self,
no_ctx=no_ctx,
as_server=as_server,
conn_gc_time=conn_gc_time,
connection_timeout=connection_timeout,
)
object.__setattr__(self, '_loop', event_loop)
con_info = object.__getattribute__(self, '_connection_info')
Expand All @@ -2222,6 +2255,7 @@ async def _initobj(self): # nocov
no_ctx=self._stored_args['no_ctx'],
as_server=self._stored_args['as_server'],
conn_gc_time=self._stored_args['conn_gc_time'],
connection_timeout=self._stored_args['connection_timeout'],
)
return self

Expand Down Expand Up @@ -2788,6 +2822,7 @@ def __init__(self,
reconnection_attempts: int = -1,
reconnection_delay: float = 0.5,
reconnection_function: callable = reconnection_function,
connection_timeout: Optional[float] = None,
):
"""Interface with a q process using the q IPC protocol.

Expand Down Expand Up @@ -2828,6 +2863,9 @@ def __init__(self,
`#!python reconnection_delay` on successive attempts to reconnect to the server. By
default this is an exponential backoff where the `#!python reconnection_delay` is
multiplied by two on each invocation
connection_timeout: Timeout in seconds for connection to `q` server. If left as default
`None`, the socket will not timeout when connecting.


Note: The `#!python username` and `#!python password` parameters are not required.
The `#!python username` and `#!python password` parameters are only required if
Expand Down Expand Up @@ -2869,6 +2907,7 @@ def __init__(self,
reconnection_attempts=reconnection_attempts,
reconnection_delay=reconnection_delay,
reconnection_function=reconnection_function,
connection_timeout=connection_timeout,
)
super().__init__()

Expand Down
2 changes: 1 addition & 1 deletion src/pykx/pykxq_m.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ EXPORT K k_pykx_init(K k_q_lib_pat, K _pykx_threading) {
return (K)0;
}

void* thread_init();
void* thread_init(void* args);
EXPORT K k_init_python(K x, K y, K z) {
pthread_mutex_init(&head_mutex, NULL);
pthread_mutex_init(&cond_mutex, NULL);
Expand Down
Loading