summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2013-06-18 23:34:42 +0100
committerMichael Williamson <mike@zwobble.org>2013-06-18 23:34:42 +0100
commitaf6662377ce4048c2214787ee2a1104a9f33839d (patch)
treee6f86bdcd439bc3167aad14dd1f716f3f67d2b10
Basic bindings for jq
-rw-r--r--.gitignore2
-rw-r--r--README.md0
-rw-r--r--jq.pyx74
-rw-r--r--makefile31
-rw-r--r--setup.py31
-rw-r--r--test-requirements.txt1
-rw-r--r--tests/jq_tests.py27
7 files changed, 166 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1f14905
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.pyc
+/README
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README.md
diff --git a/jq.pyx b/jq.pyx
new file mode 100644
index 0000000..b98d550
--- /dev/null
+++ b/jq.pyx
@@ -0,0 +1,74 @@
+cdef extern from "jv.h":
+ ctypedef struct jv:
+ pass
+ int jv_is_valid(jv)
+ char* jv_string_value(jv)
+ jv jv_dump_string(jv, int flags)
+
+
+cdef extern from "jq.h":
+ ctypedef struct jq_state:
+ pass
+
+ jq_state *jq_init()
+ void jq_teardown(jq_state **)
+ int jq_compile(jq_state *, const char* str)
+ void jq_start(jq_state *, jv value, int flags)
+ jv jq_next(jq_state *)
+
+
+cdef extern from "jv_parse.h":
+ cdef struct jv_parser:
+ pass
+
+ void jv_parser_init(jv_parser*)
+ void jv_parser_free(jv_parser*)
+ void jv_parser_set_buf(jv_parser*, const char*, int, int)
+ jv jv_parser_next(jv_parser*)
+
+
+
+def string_to_string(char* program, char* input):
+ cdef jq_state *jq
+ # TODO: error if !jq
+ jq = jq_init()
+ # TODO: error if !compiled
+ cdef int compiled = jq_compile(jq, program)
+
+ cdef jv_parser parser
+ jv_parser_init(&parser)
+ # TODO: is len a suitable replacement for strlen (unicode)?
+ jv_parser_set_buf(&parser, input, len(input), 0)
+ cdef jv value
+ results = []
+ while True:
+ value = jv_parser_next(&parser)
+ if jv_is_valid(value):
+ results.append(process(jq, value))
+ else:
+ break
+
+ jv_parser_free(&parser)
+
+ jq_teardown(&jq)
+ return "".join(results)
+
+
+cdef process(jq_state *jq, jv value):
+ cdef int jq_flags = 0
+
+ jq_start(jq, value, jq_flags);
+ cdef jv result
+ cdef int dumpopts = 0
+ cdef jv dumped
+
+ output = []
+
+ while True:
+ result = jq_next(jq)
+ if not jv_is_valid(result):
+ return "\n".join(output)
+ else:
+ dumped = jv_dump_string(result, dumpopts)
+ output.append(jv_string_value(dumped))
+ # TODO: free dumped
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..997b575
--- /dev/null
+++ b/makefile
@@ -0,0 +1,31 @@
+.PHONY: test upload clean bootstrap setup
+
+test:
+ sh -c '. _virtualenv/bin/activate; nosetests -m'\''^$$'\'' `find tests -name '\''*.py'\''`'
+
+upload: setup
+ python setup.py sdist upload
+ make clean
+
+register: setup
+ python setup.py register
+
+README:
+ pandoc --from=markdown --to=rst README.md > README
+
+clean:
+ rm -f README
+ rm -f MANIFEST
+ rm -rf dist
+
+bootstrap: _virtualenv setup
+ _virtualenv/bin/pip install -e .
+ifneq ($(wildcard test-requirements.txt),)
+ _virtualenv/bin/pip install -r test-requirements.txt
+endif
+ make clean
+
+setup: README
+
+_virtualenv:
+ virtualenv _virtualenv
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..3966809
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+import os
+from setuptools import setup
+from distutils.extension import Extension
+from Cython.Distutils import build_ext
+
+
+def read(fname):
+ return open(os.path.join(os.path.dirname(__file__), fname)).read()
+
+jq_extension = Extension(
+ "jq",
+ #sources=["_jq.c", "jq.pyx"],
+ sources=["jq.pyx"],
+ include_dirs=["../jq/"],
+ libraries=["jq"],
+ library_dirs=["../jq/"],
+)
+
+setup(
+ name='jq',
+ version='0.1.0',
+ description='jq is a lightweight and flexible command-line JSON processor.',
+ long_description=read("README"),
+ author='Michael Williamson',
+ url='http://github.com/mwilliamson/jq.py',
+ cmdclass={"build_ext": build_ext},
+ ext_modules = [jq_extension]
+)
+
diff --git a/test-requirements.txt b/test-requirements.txt
new file mode 100644
index 0000000..0798378
--- /dev/null
+++ b/test-requirements.txt
@@ -0,0 +1 @@
+nose>=1.3.0,<2
diff --git a/tests/jq_tests.py b/tests/jq_tests.py
new file mode 100644
index 0000000..0bef31f
--- /dev/null
+++ b/tests/jq_tests.py
@@ -0,0 +1,27 @@
+from nose.tools import istest, assert_equal
+
+import jq
+
+
+@istest
+def dot_operator_does_nothing():
+ assert_equal(
+ "42",
+ jq.string_to_string(".", "42")
+ )
+
+
+@istest
+def can_add_one_to_each_element_of_an_array():
+ assert_equal(
+ "[2,3,4]",
+ jq.string_to_string("[.[]+1]", "[1,2,3]")
+ )
+
+
+@istest
+def output_elements_are_separated_by_newlines():
+ assert_equal(
+ "1\n2\n3",
+ jq.string_to_string(".[]", "[1,2,3]")
+ )