Skip to content

Commit

Permalink
Yajl-Py 1.0.7 -> full wrapper to Yajl 1.0.7
Browse files Browse the repository at this point in the history
  • Loading branch information
pykler committed Oct 19, 2009
1 parent f9566d7 commit da87aeb
Show file tree
Hide file tree
Showing 7 changed files with 308 additions and 0 deletions.
17 changes: 17 additions & 0 deletions COPYING
@@ -0,0 +1,17 @@
Copyright © 2009

Hatem Nassrat <nassrat@cs.dal.ca>

These programs are free software; you can redistribute it
and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later
version.

These programs are distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more
details.


33 changes: 33 additions & 0 deletions README.rst
@@ -0,0 +1,33 @@
=======
yajl-py
=======

`yajl-py` is a Pure Python wrapper (implemented using
ctypes) to the excellent Yajl (Yet Another JSON Library) C
library.

Dependencies
------------

- python 2.5 (or compatible)
- yajl: http://lloydforge.org/projects/yajl/

Install
-------

From within the current directory run::

python setup.py install

- OR Alternatively -

easy_install .

Usage
-----

The examples directory contains a full featured JSON Parser
built using `yajl` and `yajl-py`. The code also prints some
output to stdout after parsing json from stdin. The printing
is more or less debug messages to show how the parser works.
See `examples/README.rst` for more info.
7 changes: 7 additions & 0 deletions examples/README.rst
@@ -0,0 +1,7 @@
To run the sample parser, from within this dir, run::

python yajl-test-run.py <doc.json

the example can also be run from ../ as::
python example/yajl-test-run.py <doc.json
5 changes: 5 additions & 0 deletions examples/yajl-test-run.py
@@ -0,0 +1,5 @@
import sys
import yajl_py_example

if __name__ == "__main__":
raise SystemExit(yajl_py_example.main(sys.argv[1:]))
84 changes: 84 additions & 0 deletions examples/yajl_py_example.py
@@ -0,0 +1,84 @@
import sys; sys.path = ['.', '..'] + sys.path
from yajl import *

# Sample callbacks, which reprint invalid json
# these are examples to show off the yajl parser

def yajl_null(ctx):
print "null"
return 1

def yajl_boolean(ctx, boolVal):
if boolVal:
print "true,"
else:
print "false,"
return 1

def yajl_integer(ctx, integerVal):
print "%s,"%integerVal
return 1

def yajl_double(ctx, doubleVal):
print "%s,"%doubleVal
return 1

def yajl_number(ctx, stringNum, stringLen):
nstr = string_at(stringNum, stringLen)
if '.' in nstr:
num = float(nstr)
else:
num = int(nstr)
print '%s(%s)'%(num,type(num))
return 1

def yajl_string(ctx, stringVal, stringLen):
print '"%s",'%string_at(stringVal, stringLen)
return 1

def yajl_start_map(ctx):
print "{"
return 1

def yajl_map_key(ctx, stringVal, stringLen):
print '"%s":'%string_at(stringVal, stringLen),
return 1

def yajl_end_map(ctx):
print "},"
return 1

def yajl_start_array(ctx):
print "["
return 1

def yajl_end_array(ctx):
print "],"
return 1


def main(args):
callbacks = [
yajl_null,
yajl_boolean,
#yajl_integer,
#yajl_double,
0, #replacing integer callback with NULL
0, #replacing double callback with NULL
yajl_number, # cannot have this and (double or integer)
yajl_string,
yajl_start_map,
yajl_map_key,
yajl_end_map,
yajl_start_array,
yajl_end_array
]
parser = YajlParser(callbacks)
if args:
for fn in args:
f = open(fn)
parser.parse(f=f)
f.close()
else:
parser.parse()
return 0
26 changes: 26 additions & 0 deletions setup.py
@@ -0,0 +1,26 @@
from setuptools import setup, find_packages
import sys, os

from yajl import __version__ as version

setup(name='yajl-py',
version=version,
description="Pure Python Yajl Wrapper",
long_description="""\
Pure Python wrapper to the excellent Yajl (Yet Another Json Library) C library""",
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
keywords='yajl,json',
author='Hatem Nassrat',
author_email='hnassrat@gmail.com',
url='http://github.com/pykler/yajl-py',
license='GPLv3',
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
include_package_data=True,
zip_safe=True,
install_requires=[
# -*- Extra requirements: -*-
],
entry_points="""
# -*- Entry points: -*-
""",
)
136 changes: 136 additions & 0 deletions yajl/__init__.py
@@ -0,0 +1,136 @@
'''
Pure Python wrapper to the Yajl C library
'''
__version__ = '1.0.7'

import sys
from ctypes import *

for yajlso in 'libyajl.so', 'libyajl.dylib':
try:
yajl = cdll.LoadLibrary(yajlso)
except OSError:
pass
else:
break
else:
raise OSError('Yajl shared object cannot be found. '
'Please install Yajl and confirm it is on your shared lib path.')

# from yajl_parse.h
class yajl_parser_config(Structure):
_fields_ = [
("allowComments", c_uint),
("checkUTF8", c_uint)
]

# Callback Functions
YAJL_NULL = CFUNCTYPE(c_int, c_void_p)
YAJL_BOOL = CFUNCTYPE(c_int, c_void_p, c_int)
YAJL_INT = c_int
YAJL_DBL = c_int
YAJL_NUM = CFUNCTYPE(c_int, c_void_p, POINTER(c_ubyte), c_uint)
YAJL_STR = CFUNCTYPE(c_int, c_void_p, POINTER(c_ubyte), c_uint)
YAJL_SDCT = CFUNCTYPE(c_int, c_void_p)
YAJL_DCTK = CFUNCTYPE(c_int, c_void_p, POINTER(c_ubyte), c_uint)
YAJL_EDCT = CFUNCTYPE(c_int, c_void_p)
YAJL_SARR = CFUNCTYPE(c_int, c_void_p)
YAJL_EARR = CFUNCTYPE(c_int, c_void_p)
class yajl_callbacks(Structure):
_fields_ = [
("yajl_null", YAJL_NULL),
("yajl_boolean", YAJL_BOOL),
("yajl_integer", YAJL_INT ),
("yajl_double", YAJL_DBL ),
("yajl_number", YAJL_NUM ),
("yajl_string", YAJL_STR ),
("yajl_start_map", YAJL_SDCT),
("yajl_map_key", YAJL_DCTK),
("yajl_end_map", YAJL_EDCT),
("yajl_start_array", YAJL_SARR),
("yajl_end_array", YAJL_EARR),
]

# yajl_status
(
yajl_status_ok,
yajl_status_client_canceled,
yajl_status_insufficient_data,
yajl_status_error
) = map(c_int, xrange(4))

class YajlConfigError(Exception):
pass

class YajlError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return self.value

class YajlParseCancelled(YajlError):
def __init__(self):
self.value = 'Client Callback Cancelled Parse'

class YajlParser(object):
'''
A class that utilizes the Yajl C Library
'''
def __init__(self, c, buf_siz=65536):
'''
Takes a list of callback functions `c`. The functions
need to be in order and accepting the correct number
of parameters, they should also reutrn an int. See
yajl doc for more info on parameters and return
values.
Callbacks must return an integer return code
A callback return code of:
- 0 will cancel the parse.
- 1 will allow the parse to continue.
'''
c_funcs = (
YAJL_NULL, YAJL_BOOL, YAJL_INT, YAJL_DBL, YAJL_NUM,
YAJL_STR, YAJL_SDCT, YAJL_DCTK, YAJL_EDCT, YAJL_SARR,
YAJL_EARR
)
if len(c) != len(c_funcs):
raise Exception("Must Pass %d Functions."%(len(c_funcs)))
for i in range(len(c)):
c[i] = c_funcs[i](c[i])
self.callbacks = yajl_callbacks(*c)
self.buf_siz = buf_siz
self.cfg = yajl_parser_config(1,1)

def parse(self, f=sys.stdin, ctx=None):
'''Function to parse a JSON stream.
Parameters:
`f` : file stream to read from
`buf_size` : size in bytes of read buffer
`ctx` : A ctypes pointer that will be passed to
all callback functions as the first param
Raises an expception upon error or return value of 0
from callback functions. A callback function that
returns 0 should set internal variables to denote
why they cancelled the parsing.
'''
hand = yajl.yajl_alloc( byref(self.callbacks), byref(self.cfg), ctx)
try:
while 1:
fileData = f.read(self.buf_siz-1)
if not fileData:
break
stat = yajl.yajl_parse(hand, fileData, len(fileData))
if stat not in (yajl_status_ok.value,
yajl_status_insufficient_data.value):
if stat == yajl_status_client_canceled.value:
raise YajlParseCancelled()
else:
yajl.yajl_get_error.restype = c_char_p
error = yajl.yajl_get_error(
hand, 1, fileData, len(fileData))
raise YajlError(error)
finally:
yajl.yajl_free(hand)

0 comments on commit da87aeb

Please sign in to comment.