django-piston3-0.3rc2/ 0000755 0001750 0000144 00000000000 12536673254 015470 5 ustar stefanz users 0000000 0000000 django-piston3-0.3rc2/tox.ini 0000644 0001750 0000144 00000000213 12331231443 016756 0 ustar stefanz users 0000000 0000000 [tox]
envlist = py27,py33,py34
[testenv]
changedir = tests
commands =
python bootstrap.py
python bin/buildout
python bin/test
django-piston3-0.3rc2/tests/ 0000755 0001750 0000144 00000000000 12536673254 016632 5 ustar stefanz users 0000000 0000000 django-piston3-0.3rc2/tests/test_project/ 0000755 0001750 0000144 00000000000 12536673254 021337 5 ustar stefanz users 0000000 0000000 django-piston3-0.3rc2/tests/test_project/settings.py 0000644 0001750 0000144 00000001605 12331231617 023535 0 ustar stefanz users 0000000 0000000 import os
DEBUG = True
DATABASES = {
'default':
{
'ENGINE': 'django.db.backends.sqlite3',
'NAME': '/tmp/piston.db'
}
}
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = '/tmp/piston.db'
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.admin',
'piston3',
'test_project.apps.testapp',
)
TEMPLATE_DIRS = (
os.path.join(os.path.dirname(__file__), 'templates'),
)
SITE_ID = 1
ROOT_URLCONF = 'test_project.urls'
MIDDLEWARE_CLASSES = (
'piston3.middleware.ConditionalMiddlewareCompatProxy',
'django.contrib.sessions.middleware.SessionMiddleware',
'piston3.middleware.CommonMiddlewareCompatProxy',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
SECRET_KEY = 'bla'
django-piston3-0.3rc2/tests/test_project/apps/ 0000755 0001750 0000144 00000000000 12536673254 022302 5 ustar stefanz users 0000000 0000000 django-piston3-0.3rc2/tests/test_project/apps/testapp/ 0000755 0001750 0000144 00000000000 12536673254 023762 5 ustar stefanz users 0000000 0000000 django-piston3-0.3rc2/tests/test_project/apps/testapp/signals.py 0000644 0001750 0000000 00000000143 12234405455 025572 0 ustar stefanz root 0000000 0000000 import django.dispatch
entry_request_started = django.dispatch.Signal(providing_args=['request'])
django-piston3-0.3rc2/tests/test_project/apps/testapp/forms.py 0000644 0001750 0000000 00000000322 12234405455 025257 0 ustar stefanz root 0000000 0000000 from django import forms
class EchoForm(forms.Form):
msg = forms.CharField(max_length=128)
class FormWithFileField(forms.Form):
chaff = forms.CharField(max_length=50)
le_file = forms.FileField()
django-piston3-0.3rc2/tests/test_project/apps/testapp/models.py 0000644 0001750 0000144 00000003524 12276102004 025600 0 ustar stefanz users 0000000 0000000 from django.db import models
class TestModel(models.Model):
test1 = models.CharField(max_length=1, blank=True, null=True)
test2 = models.CharField(max_length=1, blank=True, null=True)
class ExpressiveTestModel(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
never_shown = models.TextField()
class Comment(models.Model):
parent = models.ForeignKey(ExpressiveTestModel, related_name='comments')
content = models.TextField()
class AbstractModel(models.Model):
some_field = models.CharField(max_length=32, default='something here')
class Meta:
abstract = True
class InheritedModel(AbstractModel):
some_other = models.CharField(max_length=32, default='something else')
class Meta:
db_table = 'testing_abstracts'
class PlainOldObject(object):
def __emittable__(self):
return {'type': 'plain',
'field': 'a field'}
class ListFieldsModel(models.Model):
kind = models.CharField(max_length=15)
variety = models.CharField(max_length=15)
color = models.CharField(max_length=15)
class Issue58Model(models.Model):
read = models.BooleanField(default=False)
model = models.CharField(max_length=1, blank=True, null=True)
class ConditionalFieldsModel(models.Model):
field_one = models.CharField(max_length=15)
field_two = models.CharField(max_length=15)
fk_field = models.ForeignKey(TestModel)
class CircularA(models.Model):
link = models.ForeignKey('testapp.CircularB', null=True)
name = models.CharField(max_length=15)
class CircularB(models.Model):
link = models.ForeignKey('testapp.CircularC', null=True)
name = models.CharField(max_length=15)
class CircularC(models.Model):
link = models.ForeignKey('testapp.CircularA', null=True)
name = models.CharField(max_length=15)
django-piston3-0.3rc2/tests/test_project/apps/testapp/__init__.py 0000644 0001750 0000000 00000000000 12234405455 025661 0 ustar stefanz root 0000000 0000000 django-piston3-0.3rc2/tests/test_project/apps/testapp/tests.py 0000644 0001750 0000144 00000061355 12536564314 025504 0 ustar stefanz users 0000000 0000000 from __future__ import print_function
from operator import add
from itertools import product, permutations
import json
from django.test import TestCase
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
from django.conf import settings
from django.core.urlresolvers import reverse
from piston3 import oauth
from piston3.models import Consumer, Token
from piston3.forms import OAuthAuthenticationForm
try:
import yaml
except ImportError:
print("Can't run YAML testsuite")
yaml = None
import base64, tempfile
from test_project.apps.testapp.models import TestModel, ExpressiveTestModel, Comment, InheritedModel, Issue58Model, ListFieldsModel, CircularA, CircularB, CircularC, ConditionalFieldsModel
from test_project.apps.testapp import signals
class MainTests(TestCase):
def setUp(self):
self.user = User.objects.create_user('admin', 'admin@world.com', 'admin')
self.user.is_staff = True
self.user.is_superuser = True
self.user.is_active = True
self.user.save()
auth = base64.encodestring(b'admin:admin').rstrip()
self.auth_string = 'Basic %s' % auth.decode('ascii')
if hasattr(self, 'init_delegate'):
self.init_delegate()
def tearDown(self):
self.user.delete()
class OAuthTests(MainTests):
signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
def setUp(self):
super(OAuthTests, self).setUp()
self.consumer = Consumer.objects.create_consumer('Test Consumer')
self.consumer.status = 'accepted'
self.consumer.save()
def tearDown(self):
super(OAuthTests, self).tearDown()
self.consumer.delete()
def test_handshake(self):
'''Test the OAuth handshake procedure
'''
oaconsumer = oauth.OAuthConsumer(self.consumer.key, self.consumer.secret)
# Get a request key...
request = oauth.OAuthRequest.from_consumer_and_token(oaconsumer,
http_url='http://testserver/api/oauth/request_token')
request.sign_request(self.signature_method, oaconsumer, None)
response = self.client.get('/api/oauth/request_token', request.parameters)
oatoken = oauth.OAuthToken.from_string(response.content.decode('utf-8'))
token = Token.objects.get(key=oatoken.key, token_type=Token.REQUEST)
self.assertEqual(token.secret, oatoken.secret)
# Simulate user authentication...
self.assertTrue(self.client.login(username='admin', password='admin'))
request = oauth.OAuthRequest.from_token_and_callback(token=oatoken,
callback='http://printer.example.com/request_token_ready',
http_url='http://testserver/api/oauth/authorize')
request.sign_request(self.signature_method, oaconsumer, oatoken)
# Request the login page
# TODO: Parse the response to make sure all the fields exist
# response = self.client.get('/api/oauth/authorize', {
# 'oauth_token': oatoken.key,
# 'oauth_callback': 'http://printer.example.com/request_token_ready',
# })
response = self.client.post('/api/oauth/authorize', {
'oauth_token': oatoken.key,
'oauth_callback': 'http://printer.example.com/request_token_ready',
'csrf_signature': OAuthAuthenticationForm.get_csrf_signature(settings.SECRET_KEY, oatoken.key),
'authorize_access': 1,
})
# Response should be a redirect...
self.assertEqual(302, response.status_code)
self.assertTrue(response['Location'].startswith("http://printer.example.com/request_token_ready?"))
self.assertTrue(('oauth_token='+oatoken.key in response['Location']))
# Actually we can't test this last part, since it's 1.0a.
# Obtain access token...
# request = oauth.OAuthRequest.from_consumer_and_token(oaconsumer, token=oatoken,
# http_url='http://testserver/api/oauth/access_token')
# request.sign_request(self.signature_method, oaconsumer, oatoken)
# response = self.client.get('/api/oauth/access_token', request.parameters)
# oa_atoken = oauth.OAuthToken.from_string(response.content)
# atoken = Token.objects.get(key=oa_atoken.key, token_type=Token.ACCESS)
# self.assertEqual(atoken.secret, oa_atoken.secret)
class BasicAuthTest(MainTests):
def test_invalid_auth_header(self):
response = self.client.get('/api/entries/')
self.assertEqual(response.status_code, 401)
# no space
bad_auth_string = 'Basic%s' % base64.encodestring(b'admin:admin').rstrip()
response = self.client.get('/api/entries/',
HTTP_AUTHORIZATION=bad_auth_string)
self.assertEqual(response.status_code, 401)
# no colon
bad_auth_string = 'Basic %s' % base64.encodestring(b'adminadmin').rstrip()
response = self.client.get('/api/entries/',
HTTP_AUTHORIZATION=bad_auth_string)
self.assertEqual(response.status_code, 401)
# non base64 data
bad_auth_string = 'Basic FOOBARQ!'
response = self.client.get('/api/entries/',
HTTP_AUTHORIZATION=bad_auth_string)
self.assertEqual(response.status_code, 401)
class TestMultipleAuthenticators(MainTests):
def test_both_authenticators(self):
for username, password in (('admin', 'admin'),
('admin', 'secr3t'),
('admin', 'user'),
('admin', 'allwork'),
('admin', 'thisisneat')):
auth = '%s:%s' % (username, password)
auth = auth.encode('ascii') # base64 only operates on bytes
auth = base64.encodestring(auth).rstrip()
auth = auth.decode('ascii')
auth_string = 'Basic %s' % auth
response = self.client.get('/api/multiauth/',
HTTP_AUTHORIZATION=auth_string)
self.assertEqual(response.status_code, 200, 'Failed with combo of %s:%s' % (username, password))
class MultiXMLTests(MainTests):
def init_delegate(self):
self.t1_data = TestModel()
self.t1_data.save()
self.t2_data = TestModel()
self.t2_data.save()
# XML field data to dynamically include in `expected` strings:
self.test1_xml = 'None'
self.test2_xml = 'None'
def test_multixml(self):
## expected = b'\nNoneNoneNoneNone'
#PY3: Undetermined order of field elements
expected = '\n%s%s%s%s'
result = self.client.get('/api/entries.xml',
HTTP_AUTHORIZATION=self.auth_string).content
## self.assertEqual(expected, result)
self.assertIn(result, [ # Try all field orderings
(expected % add(*xml)).encode('ascii') # result is bytes
for xml in product(permutations((self.test1_xml, self.test2_xml)),
repeat=2)])
def test_singlexml(self):
obj = TestModel.objects.all()[0]
## expected = b'\nNoneNone'
#PY3: Undetermined order of field elements
expected = '\n%s%s'
result = self.client.get('/api/entry-%d.xml' % (obj.pk,),
HTTP_AUTHORIZATION=self.auth_string).content
## self.assertEqual(expected, result)
self.assertIn(result, [ # Try all field orderings
(expected % xml).encode('ascii') # result is bytes
for xml in permutations((self.test1_xml, self.test2_xml))])
class AbstractBaseClassTests(MainTests):
def init_delegate(self):
self.ab1 = InheritedModel()
self.ab1.save()
self.ab2 = InheritedModel()
self.ab2.save()
def test_field_presence(self):
result = self.client.get('/api/abstract.json',
HTTP_AUTHORIZATION=self.auth_string).content
expected = [
{
"id": 1,
"some_other": "something else",
"some_field": "something here"
},
{
"id": 2,
"some_other": "something else",
"some_field": "something here"
}
]
self.assertEqual(json.loads(result.decode('utf-8')), expected)
def test_specific_id(self):
ids = (1, 2)
expected = {
"some_other": "something else",
"some_field": "something here"
}
for id_ in ids:
result = self.client.get('/api/abstract/%d.json' % id_,
HTTP_AUTHORIZATION=self.auth_string).content
expected['id'] = id_
self.assertEqual(json.loads(result.decode('utf-8')), expected)
class IncomingExpressiveTests(MainTests):
def init_delegate(self):
e1 = ExpressiveTestModel(title="foo", content="bar")
e1.save()
e2 = ExpressiveTestModel(title="foo2", content="bar2")
e2.save()
def test_incoming_json(self):
post = { 'title': 'test', 'content': 'test',
'comments': [ { 'content': 'test1' },
{ 'content': 'test2' } ] }
outgoing = json.dumps(post)
expected = [
{
"content": "bar",
"comments": [],
"title": "foo"
},
{
"content": "bar2",
"comments": [],
"title": "foo2"
}
]
result = self.client.get('/api/expressive.json',
HTTP_AUTHORIZATION=self.auth_string).content
self.assertEqual(json.loads(result.decode('utf-8')), expected)
resp = self.client.post('/api/expressive.json', outgoing, content_type='application/json',
HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(resp.status_code, 201)
expected.append(post)
result = self.client.get('/api/expressive.json',
HTTP_AUTHORIZATION=self.auth_string).content
self.assertEqual(json.loads(result.decode('utf-8')), expected)
def test_incoming_invalid_json(self):
resp = self.client.post('/api/expressive.json',
'foo',
HTTP_AUTHORIZATION=self.auth_string,
content_type='application/json')
self.assertEqual(resp.status_code, 400)
def test_incoming_yaml(self):
if not yaml:
return
expected = b"""- comments: []
content: bar
title: foo
- comments: []
content: bar2
title: foo2
"""
self.assertEqual(self.client.get('/api/expressive.yaml',
HTTP_AUTHORIZATION=self.auth_string).content, expected)
outgoing = yaml.dump({ 'title': 'test', 'content': 'test',
'comments': [ { 'content': 'test1' },
{ 'content': 'test2' } ] })
resp = self.client.post('/api/expressive.json', outgoing, content_type='application/x-yaml',
HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(resp.status_code, 201)
expected = b"""- comments: []
content: bar
title: foo
- comments: []
content: bar2
title: foo2
- comments:
- {content: test1}
- {content: test2}
content: test
title: test
"""
self.assertEqual(self.client.get('/api/expressive.yaml',
HTTP_AUTHORIZATION=self.auth_string).content, expected)
def test_incoming_invalid_yaml(self):
resp = self.client.post('/api/expressive.yaml',
' 8**sad asj lja foo',
HTTP_AUTHORIZATION=self.auth_string,
content_type='application/x-yaml')
self.assertEqual(resp.status_code, 400)
class Issue36RegressionTests(MainTests):
"""
This testcase addresses #36 in django-piston where request.FILES is passed
empty to the handler if the request.method is PUT.
"""
def fetch_request(self, sender, request, *args, **kwargs):
self.request = request
def setUp(self):
super(self.__class__, self).setUp()
self.data = TestModel()
self.data.save()
# Register to the WSGIRequest signals to get the latest generated
# request object.
signals.entry_request_started.connect(self.fetch_request)
def tearDown(self):
super(self.__class__, self).tearDown()
self.data.delete()
signals.entry_request_started.disconnect(self.fetch_request)
def test_simple(self):
# First try it with POST to see if it works there
if True:
fp = open(__file__, 'rb') # Need binary file for client data
try:
response = self.client.post('/api/entries.xml',
{'file':fp}, HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(1, len(self.request.FILES), 'request.FILES on POST is empty when it should contain 1 file')
finally:
fp.close()
if not hasattr(self.client, 'put'):
import warnings
warnings.warn('Issue36RegressionTest partially requires Django 1.1 or newer. Skipped.')
return
# ... and then with PUT
fp = open(__file__, 'r')
try:
response = self.client.put('/api/entry-%d.xml' % self.data.pk,
{'file': fp}, HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(1, len(self.request.FILES), 'request.FILES on PUT is empty when it should contain 1 file')
finally:
fp.close()
class ValidationTest(MainTests):
def test_basic_validation_fails(self):
resp = self.client.get('/api/echo')
self.assertEqual(resp.status_code, 400)
self.assertEqual(resp.content, b'Bad Request '
b'- msg
- This field is required.
'
b'
')
def test_basic_validation_succeeds(self):
data = {'msg': 'donuts!'}
resp = self.client.get('/api/echo', data)
self.assertEqual(resp.status_code, 200)
self.assertEqual(data, json.loads(resp.content.decode('utf-8')))
class PlainOldObject(MainTests):
def test_plain_object_serialization(self):
resp = self.client.get('/api/popo')
self.assertEqual(resp.status_code, 200)
self.assertEqual({'type': 'plain', 'field': 'a field'},
json.loads(resp.content.decode('utf-8')))
class ListFieldsTest(MainTests):
def init_delegate(self):
ListFieldsModel(kind='fruit', variety='apple', color='green').save()
ListFieldsModel(kind='vegetable', variety='carrot', color='orange').save()
ListFieldsModel(kind='animal', variety='dog', color='brown').save()
def test_single_item(self):
expect = {
'color': 'green',
'kind': 'fruit',
'id': 1,
'variety': 'apple'
}
resp = self.client.get('/api/list_fields/1')
self.assertEqual(resp.status_code, 200)
self.assertEqual(json.loads(resp.content.decode('utf-8')), expect)
def test_multiple_items(self):
expect = [
{
'id': 1,
'variety': 'apple'
},
{
'id': 2,
'variety': 'carrot',
},
{
'id': 3,
'variety': 'dog',
}
]
resp = self.client.get('/api/list_fields')
self.assertEqual(resp.status_code, 200)
self.assertEqual(json.loads(resp.content.decode('utf-8')), expect)
class ErrorHandlingTests(MainTests):
"""Test proper handling of errors by Resource"""
def test_response_not_allowed(self):
resp = self.client.post('/api/echo')
self.assertEqual(resp.status_code, 405)
self.assertEqual(resp['Allow'], 'GET, HEAD')
def test_not_found_because_of_unexpected_http_method(self):
# not using self.client.head because it is not present in Django 1.0
resp = self.client.get('/api/echo', REQUEST_METHOD='HEAD')
self.assertEqual(resp.status_code, 404)
self.assertEqual(resp.content, b'')
class Issue58ModelTests(MainTests):
"""
This testcase addresses #58 in django-piston where if a model
has one of the ['read','update','delete','create'] defined
it make piston crash with a `TypeError`
"""
def init_delegate(self):
m1 = Issue58Model(read=True,model='t')
m1.save()
m2 = Issue58Model(read=False,model='f')
m2.save()
def test_incoming_json(self):
outgoing = json.dumps({ 'read': True, 'model': 'T'})
expected = [
{
"read": True,
"model": "t"
},
{
"read": False,
"model": "f"
}
]
# test GET
result = self.client.get('/api/issue58.json',
HTTP_AUTHORIZATION=self.auth_string).content
self.assertEqual(json.loads(result.decode('utf-8')), expected)
# test POST
resp = self.client.post('/api/issue58.json', outgoing, content_type='application/json',
HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(resp.status_code, 201)
class Issue188ValidateWithFiles(MainTests):
def test_whoops_no_file_upload(self):
resp = self.client.post(
reverse('file-upload-test'),
data={'chaff': 'pewpewpew'})
self.assertEqual(resp.status_code, 400)
def test_upload_with_file(self):
tmp_fs = tempfile.NamedTemporaryFile(suffix='.txt')
content = b'le_content'
tmp_fs.write(content)
tmp_fs.seek(0)
resp = self.client.post(
reverse('file-upload-test'),
data={'chaff': 'pewpewpew',
'le_file': tmp_fs})
self.assertEqual(resp.status_code, 200)
self.assertEqual(json.loads(resp.content.decode('utf-8')),
{'chaff': 'pewpewpew',
'file_size': len(content)})
class EmitterFormat(MainTests):
def test_format_in_url(self):
resp = self.client.get('/api/entries.json',
HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-type'],
'application/json; charset=utf-8')
resp = self.client.get('/api/entries.xml',
HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-type'],
'text/xml; charset=utf-8')
resp = self.client.get('/api/entries.yaml',
HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-type'],
'application/x-yaml; charset=utf-8')
def test_format_in_get_data(self):
resp = self.client.get('/api/entries/?format=json',
HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-type'],
'application/json; charset=utf-8')
resp = self.client.get('/api/entries/?format=xml',
HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-type'],
'text/xml; charset=utf-8')
resp = self.client.get('/api/entries/?format=yaml',
HTTP_AUTHORIZATION=self.auth_string)
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-type'],
'application/x-yaml; charset=utf-8')
class ConditionalFieldsTest(MainTests):
def setUp(self):
super(ConditionalFieldsTest, self).setUp()
self.test_model_obj = TestModel.objects.create(test1='a', test2='b')
self.cond_fields_obj = ConditionalFieldsModel.objects.create(
field_one='c', field_two='d', fk_field=self.test_model_obj)
def test_conditional_list_fields(self):
response = self.client.get(reverse('conditional-list'))
response_obj = json.loads(response.content.decode('utf-8'))
self.assertEqual(len(response_obj), 1)
response_struct = response_obj[0]
self.assertEqual(list(response_struct.keys()), ['field_two'])
self.assertEqual(response_struct['field_two'], 'd')
response = self.client.get(reverse('conditional-list'),
HTTP_AUTHORIZATION=self.auth_string)
response_obj = json.loads(response.content.decode('utf-8'))
self.assertEqual(len(response_obj), 1)
response_struct = response_obj[0]
self.assertEqual(len(response_struct.keys()), 2)
self.assertTrue('field_one' in response_struct.keys())
self.assertTrue('field_two' in response_struct.keys())
self.assertEqual(response_struct['field_one'], 'c')
self.assertEqual(response_struct['field_two'], 'd')
def test_conditional_detail_fields(self):
response = self.client.get(reverse('conditional-detail',
args=[self.cond_fields_obj.pk]))
response_obj = json.loads(response.content.decode('utf-8'))
self.assertEqual(list(response_obj.keys()), ['field_one'])
self.assertEqual(response_obj['field_one'], 'c')
response = self.client.get(reverse('conditional-detail',
args=[self.cond_fields_obj.pk]),
HTTP_AUTHORIZATION=self.auth_string)
response_obj = json.loads(response.content.decode('utf-8'))
self.assertEqual(len(response_obj.keys()), 3)
self.assertTrue('field_one' in response_obj.keys())
self.assertTrue('field_two' in response_obj.keys())
self.assertTrue('fk_field' in response_obj.keys())
self.assertEqual(response_obj['field_one'], 'c')
self.assertEqual(response_obj['field_two'], 'd')
self.assertEqual(type(response_obj['fk_field']), dict)
def test_format_in_accept_headers(self):
resp = self.client.get('/api/entries/',
HTTP_AUTHORIZATION=self.auth_string,
HTTP_ACCEPT='application/json')
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-type'],
'application/json; charset=utf-8')
resp = self.client.get('/api/entries/',
HTTP_AUTHORIZATION=self.auth_string,
HTTP_ACCEPT='text/xml')
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-type'],
'text/xml; charset=utf-8')
resp = self.client.get('/api/entries/',
HTTP_AUTHORIZATION=self.auth_string,
HTTP_ACCEPT='application/x-yaml')
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-type'],
'application/x-yaml; charset=utf-8')
def test_strict_accept_headers(self):
from . import urls
self.assertFalse(urls.entries.strict_accept)
self.assertEqual(urls.entries.default_emitter, 'json')
resp = self.client.get('/api/entries/',
HTTP_AUTHORIZATION=self.auth_string,
HTTP_ACCEPT='text/html')
self.assertEqual(resp.status_code, 200)
self.assertEqual(resp['Content-type'],
'application/json; charset=utf-8')
urls.entries.strict_accept = True
resp = self.client.get('/api/entries/',
HTTP_AUTHORIZATION=self.auth_string,
HTTP_ACCEPT='text/html')
self.assertEqual(resp.status_code, 406)
class CircularReferenceTest(MainTests):
def init_delegate(self):
self.a = CircularA.objects.create(name='foo')
self.b = CircularB.objects.create(name='bar')
self.c = CircularC.objects.create(name='baz')
self.a.link = self.b; self.a.save()
self.b.link = self.c; self.b.save()
self.c.link = self.a; self.c.save()
def test_circular_model_references(self):
self.assertRaises(
RuntimeError,
self.client.get,
'/api/circular_a/',
HTTP_AUTHORIZATION=self.auth_string)
django-piston3-0.3rc2/tests/test_project/apps/testapp/urls.py 0000644 0001750 0000144 00000005214 12331231641 025302 0 ustar stefanz users 0000000 0000000 from django.conf.urls import *
from piston3.resource import Resource
from piston3.authentication import HttpBasicAuthentication, HttpBasicSimple, NoAuthentication
from test_project.apps.testapp.handlers import EntryHandler, ExpressiveHandler, AbstractHandler, EchoHandler, PlainOldObjectHandler, Issue58Handler, ListFieldsHandler, FileUploadHandler, CircularAHandler, ConditionalFieldsHandler
auth = HttpBasicAuthentication(realm='TestApplication')
noauth = NoAuthentication()
entries = Resource(handler=EntryHandler, authentication=auth)
expressive = Resource(handler=ExpressiveHandler, authentication=auth)
abstract = Resource(handler=AbstractHandler, authentication=auth)
echo = Resource(handler=EchoHandler)
popo = Resource(handler=PlainOldObjectHandler)
list_fields = Resource(handler=ListFieldsHandler)
issue58 = Resource(handler=Issue58Handler)
conditional = Resource(handler=ConditionalFieldsHandler, authentication=[auth, noauth])
fileupload = Resource(handler=FileUploadHandler)
circular_a = Resource(handler=CircularAHandler)
AUTHENTICATORS = [auth,]
SIMPLE_USERS = (('admin', 'secr3t'),
('admin', 'user'),
('admin', 'allwork'),
('admin', 'thisisneat'))
for username, password in SIMPLE_USERS:
AUTHENTICATORS.append(HttpBasicSimple(realm='Test',
username=username, password=password))
multiauth = Resource(handler=PlainOldObjectHandler,
authentication=AUTHENTICATORS)
urlpatterns = patterns(
'',
url(r'^entries/$', entries),
url(r'^entries/(?P.+)/$', entries),
url(r'^entries\.(?P.+)', entries),
url(r'^entry-(?P.+)\.(?P.+)', entries),
url(r'^issue58\.(?P.+)$', issue58),
url(r'^expressive\.(?P.+)$', expressive),
url(r'^abstract\.(?P.+)$', abstract),
url(r'^abstract/(?P\d+)\.(?P.+)$', abstract),
url(r'^echo$', echo),
url(r'^file_upload/$', fileupload, name='file-upload-test'),
url(r'^multiauth/$', multiauth),
url(r'^circular_a/$', circular_a),
# oauth entrypoints
url(r'^oauth/request_token$', 'piston3.authentication.oauth_request_token'),
url(r'^oauth/authorize$', 'piston3.authentication.oauth_user_auth'),
url(r'^oauth/access_token$', 'piston3.authentication.oauth_access_token'),
url(r'^list_fields$', list_fields),
url(r'^list_fields/(?P.+)$', list_fields),
url(r'^popo$', popo),
url(r'^conditional_fields$', conditional, name='conditional-list'),
url(r'^conditional_fields/(?P\d+)$', conditional, name='conditional-detail'),
)
django-piston3-0.3rc2/tests/test_project/apps/testapp/handlers.py 0000644 0001750 0000144 00000010376 12300143436 026122 0 ustar stefanz users 0000000 0000000 from django.core.paginator import Paginator
from piston3.handler import BaseHandler
from piston3.utils import rc, validate
from .models import TestModel, ExpressiveTestModel, Comment, InheritedModel, PlainOldObject, Issue58Model, ListFieldsModel, CircularA, CircularB, CircularC, ConditionalFieldsModel
from .forms import EchoForm, FormWithFileField
from test_project.apps.testapp import signals
class EntryHandler(BaseHandler):
model = TestModel
allowed_methods = ['GET', 'PUT', 'POST']
def read(self, request, pk=None):
signals.entry_request_started.send(sender=self, request=request)
if pk is not None:
return TestModel.objects.get(pk=int(pk))
paginator = Paginator(TestModel.objects.all(), 25)
return paginator.page(int(request.GET.get('page', 1))).object_list
def update(self, request, pk):
signals.entry_request_started.send(sender=self, request=request)
def create(self, request):
signals.entry_request_started.send(sender=self, request=request)
class ExpressiveHandler(BaseHandler):
model = ExpressiveTestModel
fields = ('title', 'content', ('comments', ('content',)))
@classmethod
def comments(cls, em):
return em.comments.all()
def read(self, request):
inst = ExpressiveTestModel.objects.all()
return inst
def create(self, request):
if request.content_type and request.data:
data = request.data
em = self.model(title=data['title'], content=data['content'])
em.save()
for comment in data['comments']:
Comment(parent=em, content=comment['content']).save()
return rc.CREATED
else:
super(ExpressiveHandler, self).create(request)
class AbstractHandler(BaseHandler):
fields = ('id', 'some_other', 'some_field')
model = InheritedModel
def read(self, request, id_=None):
if id_:
return self.model.objects.get(pk=id_)
else:
return super(AbstractHandler, self).read(request)
class PlainOldObjectHandler(BaseHandler):
allowed_methods = ('GET',)
fields = ('type', 'field')
model = PlainOldObject
def read(self, request):
return self.model()
class EchoHandler(BaseHandler):
allowed_methods = ('GET', 'HEAD')
@validate(EchoForm, 'GET')
def read(self, request):
return {'msg': request.form.cleaned_data['msg']}
class ListFieldsHandler(BaseHandler):
model = ListFieldsModel
fields = ('id','kind','variety','color')
list_fields = ('id','variety')
class Issue58Handler(BaseHandler):
model = Issue58Model
def read(self, request):
return Issue58Model.objects.all()
def create(self, request):
if request.content_type:
data = request.data
em = self.model(read=data['read'], model=data['model'])
em.save()
return rc.CREATED
else:
super(Issue58Model, self).create(request)
class ConditionalFieldsHandler(BaseHandler):
model = ConditionalFieldsModel
def fields(self, request, *args, **kwargs):
if request.user.is_authenticated():
return ('field_one', 'field_two', 'fk_field')
return ('field_one',)
def list_fields(self, request, *args, **kwargs):
if request.user.is_authenticated():
return ('field_one', 'field_two')
return ('field_two',)
def read(self, request, object_id=None):
qs = self.model.objects.all()
if object_id:
qs = qs.get(pk=object_id)
return qs
class FileUploadHandler(BaseHandler):
allowed_methods = ('POST',)
@validate(FormWithFileField)
def create(self, request):
return {'chaff': request.form.cleaned_data['chaff'],
'file_size': request.form.cleaned_data['le_file'].size}
class CircularAHandler(BaseHandler):
allowed_methods = ('GET',)
fields = ('name', 'link')
model = CircularA
class CircularAHandler(BaseHandler):
allowed_methods = ('GET',)
fields = ('name', 'link')
model = CircularB
class CircularAHandler(BaseHandler):
allowed_methods = ('GET',)
fields = ('name', 'link')
model = CircularC
django-piston3-0.3rc2/tests/test_project/apps/__init__.py 0000644 0001750 0000000 00000000000 12234405455 024201 0 ustar stefanz root 0000000 0000000 django-piston3-0.3rc2/tests/test_project/templates/ 0000755 0001750 0000144 00000000000 12536673254 023335 5 ustar stefanz users 0000000 0000000 django-piston3-0.3rc2/tests/test_project/templates/404.html 0000644 0001750 0000000 00000000000 12234405455 024320 0 ustar stefanz root 0000000 0000000 django-piston3-0.3rc2/tests/test_project/templates/admin/ 0000755 0001750 0000144 00000000000 12536673254 024425 5 ustar stefanz users 0000000 0000000 django-piston3-0.3rc2/tests/test_project/templates/admin/login.html 0000644 0001750 0000000 00000000000 12234405455 026211 0 ustar stefanz root 0000000 0000000 django-piston3-0.3rc2/tests/test_project/templates/500.html 0000644 0001750 0000000 00000000000 12234405455 024315 0 ustar stefanz root 0000000 0000000 django-piston3-0.3rc2/tests/test_project/__init__.py 0000644 0001750 0000000 00000000000 12234405455 023236 0 ustar stefanz root 0000000 0000000 django-piston3-0.3rc2/tests/test_project/urls.py 0000644 0001750 0000000 00000000344 12276220544 022477 0 ustar stefanz root 0000000 0000000 from django.conf.urls import *
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns(
'',
url(r'api/', include('test_project.apps.testapp.urls')),
url(r'^admin/', include(admin.site.urls)),
)
django-piston3-0.3rc2/tests/buildout.cfg 0000644 0001750 0000000 00000000337 12536570670 020754 0 ustar stefanz root 0000000 0000000 [buildout]
parts = django
develop = ..
eggs =
django-piston3
pyyaml
[django]
recipe = djangorecipe
project = test_project
settings = settings
test = test_project.apps.testapp
eggs = ${buildout:eggs}
testrunner = test
django-piston3-0.3rc2/tests/bootstrap.py 0000644 0001750 0000000 00000005125 12275760375 021036 0 ustar stefanz root 0000000 0000000 ##############################################################################
#
# Copyright (c) 2006 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Bootstrap a buildout-based project
Simply run this script in a directory containing a buildout.cfg.
The script accepts buildout command-line options, so you can
use the -c option to specify an alternate configuration file.
$Id$
"""
from six import PY3
if PY3:
import urllib
else:
import urllib2 as urllib
import os, shutil, sys, tempfile
tmpeggs = tempfile.mkdtemp()
is_jython = sys.platform.startswith('java')
try:
import pkg_resources
except ImportError:
ez = {}
exec(urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
).read(), ez)
ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
import pkg_resources
if sys.platform == 'win32':
def quote(c):
if ' ' in c:
return '"%s"' % c # work around spawn lamosity on windows
else:
return c
else:
def quote (c):
return c
cmd = 'from setuptools.command.easy_install import main; main()'
ws = pkg_resources.working_set
if len(sys.argv) > 2 and sys.argv[1] == '--version':
VERSION = ' == %s' % sys.argv[2]
args = sys.argv[3:] + ['bootstrap']
else:
VERSION = ''
args = sys.argv[1:] + ['bootstrap']
if is_jython:
import subprocess
assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
quote(tmpeggs), 'zc.buildout' + VERSION],
env=dict(os.environ,
PYTHONPATH=
ws.find(pkg_resources.Requirement.parse('setuptools')).location
),
).wait() == 0
else:
assert os.spawnle(
os.P_WAIT, sys.executable, quote (sys.executable),
'-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION,
dict(os.environ,
PYTHONPATH=
ws.find(pkg_resources.Requirement.parse('setuptools')).location
),
) == 0
ws.add_entry(tmpeggs)
ws.require('zc.buildout' + VERSION)
import zc.buildout.buildout
zc.buildout.buildout.main(args)
shutil.rmtree(tmpeggs)
django-piston3-0.3rc2/django_piston3.egg-info/ 0000755 0001750 0000144 00000000000 12536673254 022103 5 ustar stefanz users 0000000 0000000 django-piston3-0.3rc2/django_piston3.egg-info/requires.txt 0000644 0001750 0000144 00000000043 12536673236 024500 0 ustar stefanz users 0000000 0000000 six
python-mimeparse
django >= 1.6
django-piston3-0.3rc2/django_piston3.egg-info/PKG-INFO 0000644 0001750 0000144 00000005027 12536673236 023204 0 ustar stefanz users 0000000 0000000 Metadata-Version: 1.1
Name: django-piston3
Version: 0.3rc2
Summary: Piston for Python 3.3+ and Django 1.6+ - Compatible with Python 2.7
Home-page: https://bitbucket.org/userzimmermann/django-piston3
Author: Stefan Zimmermann
Author-email: zimmermann.code@gmail.com
License: BSD
Download-URL: https://pypi.python.org/pypi/django-piston3
Description: Piston for Python 3.3+ and Django 1.6+
======================================
* Compatible with __Python 2.7__
* Forked from
([diff](https://bitbucket.org/userzimmermann/django-piston3/branches/compare/master..spookylukey#diff))
* Based on
([diff](https://bitbucket.org/userzimmermann/django-piston3/branches/compare/master..jespern#diff))
* Merged with
([diff](https://bitbucket.org/userzimmermann/django-piston3/branches/compare/master..j00bar#diff))
Usage
-----
* Import `piston3` instead of `piston`
* Original documentation:
### Issues
* Creation of `request.PUT` and `request.FILES` on __PUT__ requests,
handled by `piston3.utils.coerce_put_post`, doesn't work properly,
`Issue36RegressionTests` __fail__
* All other tests __pass__
Setup
-----
### Requirements
* [`six`](https://bitbucket.org/gutworth/six)
* [`python-mimeparse`](https://github.com/dbtsai/python-mimeparse)
* [`django >= 1.6`](http://www.djangoproject.com)
### Installation
python setup.py install
Or with [pip](http://www.pip-installer.org):
pip install .
Or from [PyPI](https://pypi.python.org/pypi/django-piston3):
pip install django-piston3
Keywords: django,piston3,piston,web,framework,api,rest,python3
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Internet :: WWW/HTTP
django-piston3-0.3rc2/django_piston3.egg-info/top_level.txt 0000644 0001750 0000144 00000000010 12536673236 024624 0 ustar stefanz users 0000000 0000000 piston3
django-piston3-0.3rc2/django_piston3.egg-info/dependency_links.txt 0000644 0001750 0000144 00000000001 12536673236 026151 0 ustar stefanz users 0000000 0000000
django-piston3-0.3rc2/django_piston3.egg-info/not-zip-safe 0000644 0001750 0000144 00000000001 12275760647 024334 0 ustar stefanz users 0000000 0000000
django-piston3-0.3rc2/django_piston3.egg-info/SOURCES.txt 0000644 0001750 0000144 00000003642 12536673240 023767 0 ustar stefanz users 0000000 0000000 AUTHORS.txt
CHANGES.md
MANIFEST.in
README.md
VERSION
ez_setup.py
requirements.txt
setup.py
tox.ini
django_piston3.egg-info/PKG-INFO
django_piston3.egg-info/SOURCES.txt
django_piston3.egg-info/dependency_links.txt
django_piston3.egg-info/not-zip-safe
django_piston3.egg-info/requires.txt
django_piston3.egg-info/top_level.txt
examples/blogserver/README.txt
examples/blogserver/__init__.py
examples/blogserver/manage.py
examples/blogserver/settings.py
examples/blogserver/urls.py
examples/blogserver/api/__init__.py
examples/blogserver/api/handlers.py
examples/blogserver/api/urls.py
examples/blogserver/blog/__init__.py
examples/blogserver/blog/models.py
examples/blogserver/blog/urls.py
examples/blogserver/blog/views.py
examples/blogserver/fixtures/initial_data.xml
examples/blogserver/templates/posts.html
examples/blogserver/templates/test_js.html
piston/__init__.py
piston/admin.py
piston/authentication.py
piston/decorator.py
piston/doc.py
piston/emitters.py
piston/forms.py
piston/handler.py
piston/handlers_doc.py
piston/managers.py
piston/middleware.py
piston/models.py
piston/oauth.py
piston/resource.py
piston/signals.py
piston/store.py
piston/test.py
piston/tests.py
piston/utils.py
piston/validate_jsonp.py
piston/fixtures/models.json
piston/fixtures/oauth.json
piston/templates/documentation.html
piston/templates/piston/authorize_token.html
piston3/__init__.py
tests/bootstrap.py
tests/buildout.cfg
tests/test_project/__init__.py
tests/test_project/settings.py
tests/test_project/urls.py
tests/test_project/apps/__init__.py
tests/test_project/apps/testapp/__init__.py
tests/test_project/apps/testapp/forms.py
tests/test_project/apps/testapp/handlers.py
tests/test_project/apps/testapp/models.py
tests/test_project/apps/testapp/signals.py
tests/test_project/apps/testapp/tests.py
tests/test_project/apps/testapp/urls.py
tests/test_project/templates/404.html
tests/test_project/templates/500.html
tests/test_project/templates/admin/login.html django-piston3-0.3rc2/piston3/ 0000755 0001750 0000144 00000000000 12536673254 017067 5 ustar stefanz users 0000000 0000000 django-piston3-0.3rc2/piston3/__init__.py 0000644 0001750 0000144 00000000500 12275454156 021171 0 ustar stefanz users 0000000 0000000 """piston3
Just a wrapper for piston/
- For `import piston3` locally from repo.
- To keep original piston/ src dir.
- setup.py installs piston/ as piston3.
.. moduleauthor:: Stefan Zimmermann
"""
from path import path as Path
__path__ = [Path(__path__[0]).dirname().abspath() / 'piston']
django-piston3-0.3rc2/requirements.txt 0000644 0001750 0000144 00000000043 12276554637 020755 0 ustar stefanz users 0000000 0000000 six
python-mimeparse
django >= 1.6
django-piston3-0.3rc2/piston/ 0000755 0001750 0000144 00000000000 12536673254 017004 5 ustar stefanz users 0000000 0000000 django-piston3-0.3rc2/piston/emitters.py 0000644 0001750 0000144 00000040146 12536602774 021216 0 ustar stefanz users 0000000 0000000 from six import string_types
import decimal, re, inspect
import copy
import collections
from itertools import chain
try:
# yaml isn't standard with python. It shouldn't be required if it
# isn't used.
import yaml
except ImportError:
yaml = None
import json
from django.db.models.query import QuerySet, RawQuerySet
from django.db.models import Model, permalink
from django.utils.xmlutils import SimplerXMLGenerator
from django.utils.encoding import smart_text
from django.core.urlresolvers import reverse, NoReverseMatch
from django.core.serializers.json import DateTimeAwareJSONEncoder
from django.http import HttpResponse
from django.core import serializers
from .utils import HttpStatusCode, Mimer
from .validate_jsonp import is_valid_jsonp_callback_value
## try:
## import cStringIO as StringIO
## except ImportError:
## import StringIO
import io
## try:
## import cPickle as pickle
## except ImportError:
## import pickle
import pickle
# Allow people to change the reverser (default `permalink`).
reverser = permalink
class Emitter(object):
"""
Super emitter. All other emitters should subclass
this one. It has the `construct` method which
conveniently returns a serialized `dict`. This is
usually the only method you want to use in your
emitter. See below for examples.
`RESERVED_FIELDS` was introduced when better resource
method detection came, and we accidentially caught these
as the methods on the handler. Issue58 says that's no good.
"""
EMITTERS = { }
RESERVED_FIELDS = set([ 'read', 'update', 'create',
'delete', 'model', 'anonymous',
'allowed_methods', 'fields', 'exclude' ])
def __init__(self, payload, typemapper, handler, fields=(), anonymous=True):
self.typemapper = typemapper
self.data = payload
self.handler = handler
self.fields = fields
self.anonymous = anonymous
if isinstance(self.data, Exception):
raise
def method_fields(self, handler, fields):
if not handler:
return { }
ret = dict()
for field in fields - Emitter.RESERVED_FIELDS:
t = getattr(handler, str(field), None)
if t and callable(t):
ret[field] = t
return ret
def construct(self):
"""
Recursively serialize a lot of types, and
in cases where it doesn't recognize the type,
it will fall back to Django's `smart_text`.
Returns `dict`.
"""
def _any(thing, fields=None):
"""
Dispatch, all types are routed through here.
"""
ret = None
# return anything we've already seen as a string only
# this prevents infinite recursion in the case of recursive
# relationships
if thing in self.stack:
raise RuntimeError(u'Circular reference detected while emitting '
'response')
self.stack.append(thing)
if isinstance(thing, (QuerySet, RawQuerySet)):
ret = _qs(thing, fields)
elif isinstance(thing, (tuple, list, set)):
ret = _list(thing, fields)
elif isinstance(thing, dict):
ret = _dict(thing, fields)
elif isinstance(thing, decimal.Decimal):
ret = str(thing)
elif isinstance(thing, Model):
ret = _model(thing, fields)
elif isinstance(thing, HttpResponse):
raise HttpStatusCode(thing)
elif inspect.isfunction(thing):
if not inspect.getargspec(thing)[0]:
ret = _any(thing())
elif hasattr(thing, '__emittable__'):
f = thing.__emittable__
if inspect.ismethod(f) and len(inspect.getargspec(f)[0]) == 1:
ret = _any(f())
elif repr(thing).startswith("