Skip to content

Commit

Permalink
Added a setup.py, defaulted JSONRPCService.provide_request to ``T…
Browse files Browse the repository at this point in the history
…rue`` (backwards-incompatible), and other minor changes
  • Loading branch information
Michael Angeletti committed Jan 25, 2013
1 parent 7124eb3 commit c8f9e67
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 35 deletions.
23 changes: 23 additions & 0 deletions LICENSE
@@ -0,0 +1,23 @@
Copyright (c) Michael Angeletti.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 changes: 9 additions & 22 deletions README.rst → README.md
@@ -1,19 +1,14 @@
============
Django-JSON-RPC-2.0
============

Currently being used by *a* production site, built-in unit tests coming soon...

Django JSON-RPC 2.0
===================

Requirements
============

* Python 2.7 +
* Django 1.3 +
------------

* Python 2.6 +
* Django 1.4 +

Example Usage
============
-------------

A basic example looks like::

Expand All @@ -40,7 +35,6 @@ A basic example looks like::
"""
... perform top secret tasks here :)


# urls.py
# =======
from django.conf import settings
Expand All @@ -54,47 +48,41 @@ A basic example looks like::
url(r'^foo.json$', csrf_exempt(foo_api), name='foo_api'),
)


# Example POST to /foo_app/foo.json

REQ -> {"jsonrpc": "2.0", "method": "get_sum", "params": {"foo": 3}, "id": 1}

RES <- {"jsonrpc": "2.0", "result": 5, "id": 1}


# In GET requests, the JSON-RPC over HTTP spec calls for arguments to be
# provided in a format like ?params=<params>&method=<method>. Since there
# is no spec for JSON-RPC 2.0 over HTTP, I've taken some liberties and made
# things a bit simpler, allowing you to simply URL encode the exact same
# JSON request object that you'd otherwise POST to the server, and provide
# it in a "json" query param (e.g. ?json={...).


# Example GET

REQ -> /rpc.json?json=%7b%22jsonrpc%22%3a+%222.0%22%2c+%22method%22%3a+%22a
dd_ints%22%2c+%22params%22%3a+%5b50%2c+25%5d%2c+%22id%22%3
a+1%7d
RES <- {"jsonrpc": "2.0", "result": 75, "id": 1}


# Example GET, with padding (ie, JSONP)

REQ -> /rpc.json?json=%7b%22jsonrpc%22%3a+%222.0%22%2c+%22method%22%3a+%22a
dd_ints%22%2c+%22params%22%3a+%5b50%2c+25%5d%2c+%22id%22%3
a+1%7d&jsoncallback=mycallback
RES < - mycallback({"jsonrpc": "2.0", "result": 75, "id": 1})


That's it. You don't need to do anything special, define a queryset method,
register anything, etc. Just write normal methods, and wrap the ones you wish
to expose with the `@jrpc` decorator (as shown above). If you define a method
and wrap it in `@jrpc` and the syntax of the method signature you provide to
`@jrpc` is incorrect, it'll raise an error (during "compile" time).


Features
============
--------

When your API is in debug mode you'll receive a `debug` key in the response
JSON object, which contains information about the queries that were run during
Expand All @@ -106,14 +94,13 @@ class. Then, write your API methods to accept `request` as the first argument.
Supports JSON-P, with any callback name you'd like to use, with an attribute
`padding_names` on your API class (default is `('callback', 'jsoncallback')`)


Freebies
============
--------

Every API you create comes with a method called `system.describe` which returns
a JSON-RPC 2.0 spec description of the API's methods, the arguments they take,
whether each argument is optional, which type the argument should be, etc. This
method can be overridden just like any other.

:author: Michael Angeletti
:date: 2012/10/11
:date: 2013/01/24
2 changes: 1 addition & 1 deletion jsonrpc/__init__.py
@@ -1 +1 @@

VERSION = (1, 0)
26 changes: 14 additions & 12 deletions jsonrpc/service.py
Expand Up @@ -11,8 +11,10 @@
from django.http import HttpResponse

from .decorators import jrpc
from .errors import (InternalError, InvalidParamsError, InvalidRequestError,
JSONRPCError, MethodNotFoundError, ParseError)
from .errors import (
InternalError, InvalidParamsError, InvalidRequestError, JSONRPCError,
MethodNotFoundError, ParseError
)
from .json_types import JSONType
from .encoders import RobustEncoder

Expand Down Expand Up @@ -86,7 +88,7 @@ def add_numbers(self, request, a, b):
# When set to ``True`` this provides the Django request object as the
# first argument of each RPC method (after ``self``), so that the RPC
# methods can use request (e.g., ``request.META.get('REMOTE_ADDR', None)``)
provide_request = False
provide_request = True

# Service Description: http://json-rpc.org/wd/JSON-RPC-1-1-WD-20060807.html
service_sdversion = u'1.0' # Service description version (always "1.0").
Expand Down Expand Up @@ -205,7 +207,7 @@ def _get_json_req(self, request):
u'The "json" URL argument cannot be empty')
try:
json_req = json.loads(urllib2.unquote(urlencoded_json))
if not type(json_req) == dict:
if not isinstance(json_req, dict):
raise InvalidRequestError(
details=u'The JSON provided must be an object')
return json_req
Expand All @@ -217,7 +219,7 @@ def _get_json_req(self, request):
except ValueError:
raise ParseError
else:
if not type(json_req) == dict:
if not isinstance(json_req, dict):
raise InvalidRequestError(
details=u'The JSON provided must be an object')
return json_req
Expand Down Expand Up @@ -257,7 +259,7 @@ def _valid_jsonrpc_params(self, json_req):
raise InvalidRequestError(
details=u'`params` argument required, even when a method '
'doesn\'t require any params')
if type(params) not in (dict, list):
if not isinstance(params, (dict, list)):
raise InvalidParamsError(
details=u'`params` argument must be an array or an object')
return params
Expand All @@ -271,7 +273,7 @@ def _valid_jsonrpc_method(self, json_req):
method = json_req['method']
except KeyError:
raise InvalidRequestError(details=u'`method` argument required')
if not type(method) in (unicode, str):
if not isinstance(method, (unicode, str)):
raise InvalidRequestError(
details=u'The `method` argument must be a string')
return method
Expand Down Expand Up @@ -365,7 +367,7 @@ def _valid_params(method, params):
missmatch is found.
"""
# ``list`` (JavaScript Array) based "params"
if type(params) is list:
if isinstance(params, list):
params_list = []
for idx, defined in enumerate(method.rpc_params):
try:
Expand All @@ -380,7 +382,7 @@ def _valid_params(method, params):
raise InvalidParamsError(
details=u'Parameter `{0}` is required, but was '
'not provided'.format(defined['name']))
if not JSONType(defined['type']) == type(provided):
if not isinstance(provided, JSONType(defined['type'])):
if defined['optional'] and provided is None:
pass # Optional params are allowed to be "nil"
else:
Expand All @@ -390,7 +392,7 @@ def _valid_params(method, params):
params_list.append(provided)
return params_list
# ``dict`` (JavaScript object) based "params"
elif type(params) is dict:
elif isinstance(params, dict):
params_dict = {}
for defined in method.rpc_params:
name = defined['name']
Expand All @@ -403,7 +405,7 @@ def _valid_params(method, params):
raise InvalidParamsError(
details=u'Parameter `{0}` is required, but was '
'not provided'.format(defined['name']))
if not JSONType(defined['type']) == type(provided):
if not isinstance(provided, JSONType(defined['type'])):
if defined['optional'] and provided is None:
pass # Optional params are allowed to be "nil"
else:
Expand Down Expand Up @@ -459,7 +461,7 @@ def _dispatch(self, request, method_name, params):
params = self._valid_params(method, params)

# Call method with params provided as a **kwargs
if type(params) is dict:
if isinstance(params, dict):
if self.provide_request:
# Include the request as the first argument
return method(self, request, **params)
Expand Down
26 changes: 26 additions & 0 deletions setup.py
@@ -0,0 +1,26 @@
from setuptools import setup, find_packages


# Dynamically calculate the version based on jsonrpc.VERSION
version = '.'.join([str(v) for v in __import__('jsonrpc').VERSION])

setup(
name = 'jsonrpc',
description = (
'A JSON-RPC 2.0 server that is loosely coupled to Django'
),
version = version,
author = 'Michael Angeletti',
author_email = 'michael [at] angelettigroup [dot] com',
url = 'http://github.com/orokusaki/django-jsonrpc-2-0/',
classifiers = [
'Environment :: Web Environment',
'Framework :: Django',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Utilities'
],
packages=find_packages(),
)

0 comments on commit c8f9e67

Please sign in to comment.