Skip to content

Commit 5ac3dd3

Browse files
feat(query): implement set_timeout_micros
1 parent e549892 commit 5ac3dd3

File tree

5 files changed

+46
-14
lines changed

5 files changed

+46
-14
lines changed

docs/classes/tree_sitter.Query.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ Query
7979
.. automethod:: set_max_start_depth
8080

8181
.. versionadded:: 0.23.0
82+
.. automethod:: set_timeout_micros
83+
84+
.. versionadded:: 0.23.1
8285

8386
Attributes
8487
----------
@@ -95,3 +98,6 @@ Query
9598
.. autoattribute:: pattern_count
9699

97100
.. versionadded:: 0.23.0
101+
.. autoattribute:: timeout_micros
102+
103+
.. versionadded:: 0.23.1

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def process_signature(_app, _what, name, _obj, _options, _signature, return_anno
6161
if name == "tree_sitter.Language":
6262
return "(ptr)", return_annotation
6363
if name == "tree_sitter.Query":
64-
return "(language, source)", return_annotation
64+
return "(language, source, *, timeout_micros=None)", return_annotation
6565
if name == "tree_sitter.Parser":
6666
return "(language, *, included_ranges=None, timeout_micros=None)", return_annotation
6767
if name == "tree_sitter.Range":

tree_sitter/__init__.pyi

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,21 +258,24 @@ class QueryPredicate(Protocol):
258258

259259
@final
260260
class Query:
261-
def __init__(self, language: Language, source: str) -> None: ...
261+
def __new__(cls, language: Language, source: str) -> Self: ...
262262
@property
263263
def pattern_count(self) -> int: ...
264264
@property
265265
def capture_count(self) -> int: ...
266266
@property
267+
def timeout_micros(self) -> int: ...
268+
@property
267269
def match_limit(self) -> int: ...
268270
@property
269271
def did_exceed_match_limit(self) -> bool: ...
270-
def set_match_limit(self, match_limit: int | None) -> Self: ...
271-
def set_max_start_depth(self, max_start_depth: int | None) -> Self: ...
272-
def set_byte_range(self, byte_range: tuple[int, int] | None) -> Self: ...
272+
def set_timeout_micros(self, timeout_micros: int) -> Self: ...
273+
def set_match_limit(self, match_limit: int) -> Self: ...
274+
def set_max_start_depth(self, max_start_depth: int) -> Self: ...
275+
def set_byte_range(self, byte_range: tuple[int, int]) -> Self: ...
273276
def set_point_range(
274277
self,
275-
point_range: tuple[Point | tuple[int, int], Point | tuple[int, int]] | None
278+
point_range: tuple[Point | tuple[int, int], Point | tuple[int, int]]
276279
) -> Self: ...
277280
def disable_pattern(self, index: int) -> Self: ...
278281
def disable_capture(self, capture: str) -> Self: ...

tree_sitter/binding/parser.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ int parser_set_timeout_micros(Parser *self, PyObject *arg, void *Py_UNUSED(paylo
205205
return -1;
206206
}
207207

208-
ts_parser_set_timeout_micros(self->parser, PyLong_AsUnsignedLong(arg));
208+
ts_parser_set_timeout_micros(self->parser, PyLong_AsSize_t(arg));
209209
return 0;
210210
}
211211

@@ -312,12 +312,7 @@ int parser_set_language(Parser *self, PyObject *arg, void *Py_UNUSED(payload)) {
312312
int parser_init(Parser *self, PyObject *args, PyObject *kwargs) {
313313
ModuleState *state = GET_MODULE_STATE(self);
314314
PyObject *language = NULL, *included_ranges = NULL, *timeout_micros = NULL;
315-
char *keywords[] = {
316-
"language",
317-
"included_ranges",
318-
"timeout_micros",
319-
NULL,
320-
};
315+
char *keywords[] = {"language", "included_ranges", "timeout_micros", NULL};
321316
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O!$OO:__init__", keywords,
322317
state->language_type, &language, &included_ranges,
323318
&timeout_micros)) {

tree_sitter/binding/query.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,16 @@ PyObject *query_pattern_assertions(Query *self, PyObject *args) {
611611
return item;
612612
}
613613

614+
PyObject *query_set_timeout_micros(Query *self, PyObject *args) {
615+
uint32_t timeout_micros;
616+
if (!PyArg_ParseTuple(args, "I:set_timeout_micros", &timeout_micros)) {
617+
return NULL;
618+
}
619+
ts_query_cursor_set_timeout_micros(self->cursor, timeout_micros);
620+
Py_INCREF(self);
621+
return (PyObject *)self;
622+
}
623+
614624
PyObject *query_set_match_limit(Query *self, PyObject *args) {
615625
uint32_t match_limit;
616626
if (!PyArg_ParseTuple(args, "I:set_match_limit", &match_limit)) {
@@ -730,6 +740,10 @@ PyObject *query_get_capture_count(Query *self, void *Py_UNUSED(payload)) {
730740
return PyLong_FromSize_t(ts_query_capture_count(self->query));
731741
}
732742

743+
PyObject *query_get_timeout_micros(Query *self, void *Py_UNUSED(payload)) {
744+
return PyLong_FromSize_t(ts_query_cursor_timeout_micros(self->cursor));
745+
}
746+
733747
PyObject *query_get_match_limit(Query *self, void *Py_UNUSED(payload)) {
734748
return PyLong_FromSize_t(ts_query_cursor_match_limit(self->cursor));
735749
}
@@ -738,6 +752,9 @@ PyObject *query_get_did_exceed_match_limit(Query *self, void *Py_UNUSED(payload)
738752
return PyLong_FromSize_t(ts_query_cursor_did_exceed_match_limit(self->cursor));
739753
}
740754

755+
PyDoc_STRVAR(query_set_timeout_micros_doc, "set_timeout_micros(self, timeout_micros)\n--\n\n"
756+
"Set the maximum duration in microseconds that query "
757+
"execution should be allowed to take before halting.");
741758
PyDoc_STRVAR(query_set_match_limit_doc, "set_match_limit(self, match_limit)\n--\n\n"
742759
"Set the maximum number of in-progress matches." DOC_RAISES
743760
"ValueError\n\n If set to ``0``.");
@@ -798,6 +815,12 @@ PyDoc_STRVAR(query_is_pattern_guaranteed_at_step_doc,
798815
"Check if a pattern is guaranteed to match once a given byte offset is reached.");
799816

800817
static PyMethodDef query_methods[] = {
818+
{
819+
.ml_name = "set_timeout_micros",
820+
.ml_meth = (PyCFunction)query_set_timeout_micros,
821+
.ml_flags = METH_VARARGS,
822+
.ml_doc = query_set_timeout_micros_doc,
823+
},
801824
{
802825
.ml_name = "set_match_limit",
803826
.ml_meth = (PyCFunction)query_set_match_limit,
@@ -902,13 +925,18 @@ static PyGetSetDef query_accessors[] = {
902925
PyDoc_STR("The number of patterns in the query."), NULL},
903926
{"capture_count", (getter)query_get_capture_count, NULL,
904927
PyDoc_STR("The number of captures in the query."), NULL},
928+
{"timeout_micros", (getter)query_get_timeout_micros, NULL,
929+
PyDoc_STR("The maximum duration in microseconds that query "
930+
"execution should be allowed to take before halting."),
931+
NULL},
905932
{"match_limit", (getter)query_get_match_limit, NULL,
906933
PyDoc_STR("The maximum number of in-progress matches."), NULL},
907934
{"did_exceed_match_limit", (getter)query_get_did_exceed_match_limit, NULL,
908935
PyDoc_STR("Check if the query exceeded its maximum number of "
909936
"in-progress matches during its last execution."),
910937
NULL},
911-
{NULL}};
938+
{NULL},
939+
};
912940

913941
static PyType_Slot query_type_slots[] = {
914942
{Py_tp_doc, PyDoc_STR("A set of patterns that match nodes in a syntax tree." DOC_RAISES

0 commit comments

Comments
 (0)