diff options
| author | Michael Williamson <mike@zwobble.org> | 2023-05-01 18:54:18 +0100 |
|---|---|---|
| committer | Michael Williamson <mike@zwobble.org> | 2023-05-01 19:08:37 +0100 |
| commit | bebc3653452c60e64e14a1a7d1b476ee75b2ae8a (patch) | |
| tree | e2e3b89d183c23fab20b95bb4ed59b0cc279a80f | |
| parent | 888d37322643fd099008d76f12184648047d7204 (diff) | |
Add slurp support
| -rw-r--r-- | README.rst | 9 | ||||
| -rw-r--r-- | jq.pyx | 38 | ||||
| -rw-r--r-- | tests/jq_tests.py | 24 |
3 files changed, 64 insertions, 7 deletions
@@ -99,6 +99,15 @@ Call ``.input_text()`` to supply unparsed JSON text: assert jq.compile(".").input_text("0.42").first() == 0.42 assert jq.compile(".").input_text("true").first() == True assert jq.compile(".").input_text('"hello"').first() == "hello" + assert jq.compile(".").input_text("1\n2\n3").all() == [1, 2, 3] + +Pass ``slurp=True`` to ``.input_text()`` to read the entire input into an array: + +.. code-block:: python + + import jq + + assert jq.compile(".").input_text("1\n2\n3", slurp=True).first() == [1, 2, 3] You can also call the older ``input()`` method by passing: @@ -32,6 +32,8 @@ cdef extern from "jv.h": int jv_string_length_bytes(jv) int jv_is_integer(jv) double jv_number_value(jv) + jv jv_array() + jv jv_array_append(jv, jv) int jv_array_length(jv) jv jv_array_get(jv, int) int jv_object_iter(jv) @@ -263,8 +265,8 @@ cdef class _Program(object): fileobj.write("\n") return self.input_text(fileobj.getvalue()) - def input_text(self, text): - return _ProgramWithInput(self._jq_state_pool, text.encode("utf8")) + def input_text(self, text, *, slurp=False): + return _ProgramWithInput(self._jq_state_pool, text.encode("utf8"), slurp=slurp) @property def program_string(self): @@ -287,16 +289,18 @@ cdef class _Program(object): cdef class _ProgramWithInput(object): cdef _JqStatePool _jq_state_pool cdef object _bytes_input + cdef bint _slurp - def __cinit__(self, jq_state_pool, bytes_input): + def __cinit__(self, jq_state_pool, bytes_input, *, bint slurp): self._jq_state_pool = jq_state_pool self._bytes_input = bytes_input + self._slurp = slurp def __iter__(self): return self._make_iterator() cdef _ResultIterator _make_iterator(self): - return _ResultIterator(self._jq_state_pool, self._bytes_input) + return _ResultIterator(self._jq_state_pool, self._bytes_input, slurp=self._slurp) def text(self): # Performance testing suggests that using _jv_to_python (within the @@ -317,16 +321,18 @@ cdef class _ResultIterator(object): cdef jq_state* _jq cdef jv_parser* _parser cdef bytes _bytes_input + cdef bint _slurp cdef bint _ready def __dealloc__(self): self._jq_state_pool.release(self._jq) jv_parser_free(self._parser) - def __cinit__(self, _JqStatePool jq_state_pool, bytes bytes_input): + def __cinit__(self, _JqStatePool jq_state_pool, bytes bytes_input, *, bint slurp): self._jq_state_pool = jq_state_pool self._jq = jq_state_pool.acquire() self._bytes_input = bytes_input + self._slurp = slurp self._ready = False cdef jv_parser* parser = jv_parser_new(0) cdef char* cbytes_input = PyBytes_AsString(bytes_input) @@ -356,10 +362,28 @@ cdef class _ResultIterator(object): cdef bint _ready_next_input(self) except 1: cdef int jq_flags = 0 + cdef jv value + + if self._slurp: + value = jv_array() + + while True: + try: + next_value = self._parse_next_input() + value = jv_array_append(value, next_value) + except StopIteration: + self._slurp = False + break + else: + value = self._parse_next_input() + + jq_start(self._jq, value, jq_flags) + return 0 + + cdef inline jv _parse_next_input(self) except *: cdef jv value = jv_parser_next(self._parser) if jv_is_valid(value): - jq_start(self._jq, value, jq_flags) - return 0 + return value elif jv_invalid_has_msg(jv_copy(value)): error_message = jv_invalid_get_msg(value) message = jv_string_to_py_string(error_message) diff --git a/tests/jq_tests.py b/tests/jq_tests.py index d97f2bd..5f6bb16 100644 --- a/tests/jq_tests.py +++ b/tests/jq_tests.py @@ -97,6 +97,30 @@ def test_input_can_be_text_with_multiple_values_separated_by_newlines(): assert_equal([1, 2, 3], result) +def test_slurping_input_text_reads_input_as_single_array(): + program = jq.compile(".") + + result = program.input_text("1\n2\n3\n", slurp=True).all() + + assert_equal([[1, 2, 3]], result) + + +def test_can_slurp_without_trailing_newline(): + program = jq.compile(".") + + result = program.input_text("1\n2\n3", slurp=True).all() + + assert_equal([[1, 2, 3]], result) + + +def test_can_slurp_and_produce_multiple_output_values(): + program = jq.compile(".[]") + + result = program.input_text("1\n2\n3", slurp=True).all() + + assert_equal([1, 2, 3], result) + + def test_when_text_method_is_used_on_result_then_output_is_serialised_to_json_string(): assert_equal( '"42"', |
