Most of the actual work so far has been markup really.

This commit is contained in:
markotibold 2011-05-18 22:13:48 +02:00
parent 49d4e50342
commit 92c015e049
6 changed files with 92 additions and 80 deletions

View File

@ -4,7 +4,7 @@ The :mod:`authentication` module provides a set of pluggable authentication clas
Authentication behavior is provided by mixing the :class:`mixins.AuthMixin` class into a :class:`View` class.
The set of authentication methods which are used is then specified by setting the
:attr:`authentication` attribute on the :class:`View` class, and listing a set of authentication classes.
:attr:`authentication` attribute on the :class:`View` class, and listing a set of :class:`authentication` classes.
"""
from django.contrib.auth import authenticate
@ -26,20 +26,19 @@ class BaseAuthenticaton(object):
def __init__(self, view):
"""
:param view: :class:`Authentication` classes are always passed the current view on creation.
:class:`Authentication` classes are always passed the current view on creation.
"""
self.view = view
def authenticate(self, request):
"""
:param request: Request to be authenticated
:rtype: :obj:`User` or None [*]_
Authenticate the :obj:`request` and return a :obj:`User` instance or :const:`None`. [*]_
.. [*] The authentication context *will* typically be a :obj:`User`,
.. [*] The authentication context *will* typically be a :obj:`User` object,
but it need not be. It can be any user-like object so long as the
permissions classes on the view can handle the object and use
permissions classes (see the :mod:`permissions` module) on the view can handle the object and use
it to determine if the request has the required permissions or not.
This can be an important distinction if you're implementing some token
based authentication mechanism, where the authentication context
may be more involved than simply mapping to a :obj:`User`.
@ -55,7 +54,7 @@ class BasicAuthenticaton(BaseAuthenticaton):
def authenticate(self, request):
"""
Returns a :obj:`User` if a correct username and password have been supplied
using HTTP Basic authentication. Otherwise returns `None`.
using HTTP Basic authentication. Otherwise returns :const:`None`.
"""
from django.utils.encoding import smart_unicode, DjangoUnicodeDecodeError
@ -85,7 +84,7 @@ class UserLoggedInAuthenticaton(BaseAuthenticaton):
def authenticate(self, request):
"""
Returns a :obj:`User` if the request session currently has a logged in user, otherwise `None`.
Returns a :obj:`User` object if the request session currently has a logged in user. Otherwise returns :const:`None`.
"""
# TODO: Switch this back to request.POST, and let FormParser/MultiPartParser deal with the consequences.
if getattr(request, 'user', None) and request.user.is_active:

View File

@ -1,4 +1,6 @@
"""Compatability module to provide support for backwards compatability with older versions of django/python"""
"""
Compatability module to provide support for backwards compatability with older versions of django/python
"""
# cStringIO only if it's available
try:
@ -27,24 +29,25 @@ except ImportError:
# Lovely stuff
class RequestFactory(Client):
"""
Class that lets you create mock Request objects for use in testing.
Class that lets you create mock :obj:`Request` objects for use in testing.
Usage:
Usage::
rf = RequestFactory()
get_request = rf.get('/hello/')
post_request = rf.post('/submit/', {'foo': 'bar'})
rf = RequestFactory()
get_request = rf.get('/hello/')
post_request = rf.post('/submit/', {'foo': 'bar'})
This class re-uses the django.test.client.Client interface, docs here:
http://www.djangoproject.com/documentation/testing/#the-test-client
This class re-uses the :class:`django.test.client.Client` interface. Of which
you can find the docs here__.
Once you have a request object you can pass it to any view function,
just as if that view had been hooked up using a URLconf.
__ http://www.djangoproject.com/documentation/testing/#the-test-client
Once you have a :obj:`request` object you can pass it to any :func:`view` function,
just as if that :func:`view` had been hooked up using a URLconf.
"""
def request(self, **request):
"""
Similar to parent class, but returns the request object as soon as it
Similar to parent class, but returns the :obj:`request` object as soon as it
has created it.
"""
environ = {
@ -148,9 +151,11 @@ try:
import re
class CustomSetextHeaderProcessor(markdown.blockprocessors.BlockProcessor):
"""Override markdown's SetextHeaderProcessor, so that ==== headers are <h2> and ---- headers are <h3>.
"""
Override `markdown`'s :class:`SetextHeaderProcessor`, so that ==== headers are <h2> and ---- headers are <h3>.
We use <h1> for the resource name."""
We use <h1> for the resource name.
"""
# Detect Setext-style header. Must be first 2 lines of block.
RE = re.compile(r'^.*?\n[=-]{3,}', re.MULTILINE)
@ -172,8 +177,11 @@ try:
blocks.insert(0, '\n'.join(lines[2:]))
def apply_markdown(text):
"""Simple wrapper around markdown.markdown to apply our CustomSetextHeaderProcessor,
and also set the base level of '#' style headers to <h2>."""
"""
Simple wrapper around :func:`markdown.markdown` to apply our :class:`CustomSetextHeaderProcessor`,
and also set the base level of '#' style headers to <h2>.
"""
extensions = ['headerid(level=2)']
safe_mode = False,
output_format = markdown.DEFAULT_OUTPUT_FORMAT

View File

@ -1,5 +1,6 @@
"""
The mixins module provides a set of reusable mixin classes that can be added to a ``View``.
The :mod:`mixins` module provides a set of reusable `mixin`
classes that can be added to a `View`.
"""
from django.contrib.auth.models import AnonymousUser
@ -41,7 +42,7 @@ __all__ = (
class RequestMixin(object):
"""
Mixin class to provide request parsing behavior.
`Mixin` class to provide request parsing behavior.
"""
_USE_FORM_OVERLOADING = True
@ -52,7 +53,7 @@ class RequestMixin(object):
"""
The set of request parsers that the view can handle.
Should be a tuple/list of classes as described in the ``parsers`` module.
Should be a tuple/list of classes as described in the :mod:`parsers` module.
"""
parsers = ()
@ -61,8 +62,8 @@ class RequestMixin(object):
"""
Returns the HTTP method.
This should be used instead of ``request.method``, as it allows the method
to be overridden by using a hidden form field on a form POST request.
This should be used instead of just reading :const:`request.method`, as it allows the `method`
to be overridden by using a hidden `form` field on a form POST request.
"""
if not hasattr(self, '_method'):
self._load_method_and_content_type()
@ -100,7 +101,7 @@ class RequestMixin(object):
def FILES(self):
"""
Parses the request body and returns the files.
Similar to request.FILES, except that it handles arbitrary parsers,
Similar to ``request.FILES``, except that it handles arbitrary parsers,
and also works on methods other than POST (eg PUT).
"""
if not hasattr(self, '_files'):
@ -215,10 +216,10 @@ class RequestMixin(object):
class ResponseMixin(object):
"""
Adds behavior for pluggable Renderers to a :class:`.BaseView` or Django :class:`View`. class.
Adds behavior for pluggable `Renderers` to a :class:`views.BaseView` or Django :class:`View` class.
Default behavior is to use standard HTTP Accept header content negotiation.
Also supports overriding the content type by specifying an _accept= parameter in the URL.
Also supports overriding the content type by specifying an ``_accept=`` parameter in the URL.
Ignores Accept headers from Internet Explorer user agents and uses a sensible browser Accept header instead.
"""
@ -228,7 +229,7 @@ class ResponseMixin(object):
"""
The set of response renderers that the view can handle.
Should be a tuple/list of classes as described in the ``renderers`` module.
Should be a tuple/list of classes as described in the :mod:`renderers` module.
"""
renderers = ()
@ -237,7 +238,7 @@ class ResponseMixin(object):
# out of the box with existing Django classes that use render_to_response.
def render(self, response):
"""
Takes a ``Response`` object and returns an ``HttpResponse``.
Takes a :obj:`Response` object and returns an :obj:`HttpResponse`.
"""
self.response = response
@ -354,21 +355,21 @@ class ResponseMixin(object):
class AuthMixin(object):
"""
Simple mixin class to add authentication and permission checking to a ``View`` class.
Simple :class:`mixin` class to add authentication and permission checking to a :class:`View` class.
"""
"""
The set of authentication types that this view can handle.
Should be a tuple/list of classes as described in the ``authentication`` module.
Should be a tuple/list of classes as described in the :mod:`authentication` module.
"""
authentication = ()
"""
The set of permissions that will be enforced on this view.
Should be a tuple/list of classes as described in the ``permissions`` module.
Should be a tuple/list of classes as described in the :mod:`permissions` module.
"""
permissions = ()
@ -376,8 +377,8 @@ class AuthMixin(object):
@property
def user(self):
"""
Returns the user for the current request, as determined by the set of
authentication classes applied to the ``View``.
Returns the :obj:`user` for the current request, as determined by the set of
:class:`authentication` classes applied to the :class:`View`.
"""
if not hasattr(self, '_user'):
self._user = self._authenticate()
@ -413,12 +414,10 @@ class AuthMixin(object):
class ResourceMixin(object):
"""
Provides request validation and response filtering behavior.
"""
"""
Should be a class as described in the ``resources`` module.
Should be a class as described in the :mod:`resources` module.
The ``resource`` is an object that maps a view onto it's representation on the server.
The :obj:`resource` is an object that maps a view onto it's representation on the server.
It provides validation on the content of incoming requests,
and filters the object representation into a serializable object for the response.
@ -436,8 +435,8 @@ class ResourceMixin(object):
def validate_request(self, data, files):
"""
Given the request data return the cleaned, validated content.
Typically raises a ErrorResponse with status code 400 (Bad Request) on failure.
Given the request *data* return the cleaned, validated content.
Typically raises an :class:`response.ErrorResponse` with status code 400 (Bad Request) on failure.
"""
resource = self.resource(self)
return resource.validate_request(data, files)
@ -459,8 +458,8 @@ class ResourceMixin(object):
class InstanceMixin(object):
"""
Mixin class that is used to identify a view class as being the canonical identifier
for the resources it is mapped too.
`Mixin` class that is used to identify a `View` class as being the canonical identifier
for the resources it is mapped to.
"""
@classmethod
@ -482,7 +481,7 @@ class InstanceMixin(object):
class ReadModelMixin(object):
"""
Behavior to read a model instance on GET requests
Behavior to read a `model` instance on GET requests
"""
def get(self, request, *args, **kwargs):
model = self.resource.model
@ -501,7 +500,7 @@ class ReadModelMixin(object):
class CreateModelMixin(object):
"""
Behavior to create a model instance on POST requests
Behavior to create a `model` instance on POST requests
"""
def post(self, request, *args, **kwargs):
model = self.resource.model
@ -525,7 +524,7 @@ class CreateModelMixin(object):
class UpdateModelMixin(object):
"""
Behavior to update a model instance on PUT requests
Behavior to update a `model` instance on PUT requests
"""
def put(self, request, *args, **kwargs):
model = self.resource.model
@ -550,7 +549,7 @@ class UpdateModelMixin(object):
class DeleteModelMixin(object):
"""
Behavior to delete a model instance on DELETE requests
Behavior to delete a `model` instance on DELETE requests
"""
def delete(self, request, *args, **kwargs):
model = self.resource.model
@ -570,7 +569,7 @@ class DeleteModelMixin(object):
class ListModelMixin(object):
"""
Behavior to list a set of model instances on GET requests
Behavior to list a set of `model` instances on GET requests
"""
# NB. Not obvious to me if it would be better to set this on the resource?

View File

@ -5,8 +5,9 @@ to general HTTP requests.
We need a method to be able to:
1) Determine the parsed content on a request for methods other than POST (eg typically also PUT)
2) Determine the parsed content on a request for media types other than application/x-www-form-urlencoded
1.) Determine the parsed content on a request for methods other than POST (eg typically also PUT)
2.) Determine the parsed content on a request for media types other than application/x-www-form-urlencoded
and multipart/form-data. (eg also handle multipart/json)
"""
@ -22,47 +23,51 @@ __all__ = (
'BaseParser',
'JSONParser',
'PlainTextParser',
'DataFlatener',
'FormParser',
'MultiPartParser'
'MultiPartParser',
)
class BaseParser(object):
"""
All parsers should extend BaseParser, specifying a media_type attribute,
and overriding the parse() method.
All parsers should extend :class:`BaseParser`, specifying a :attr:`media_type` attribute,
and overriding the :meth:`parse` method.
"""
media_type = None
def __init__(self, view):
"""
Initialize the parser with the ``View`` instance as state,
in case the parser needs to access any metadata on the ``View`` object.
in case the parser needs to access any metadata on the :obj:`View` object.
"""
self.view = view
def can_handle_request(self, content_type):
"""
Returns `True` if this parser is able to deal with the given media type.
Returns :const:`True` if this parser is able to deal with the given *content_type*.
The default implementation for this function is to check the ``media_type``
argument against the ``media_type`` attribute set on the class to see if
The default implementation for this function is to check the *content_type*
argument against the :attr:`media_type` attribute set on the class to see if
they match.
This may be overridden to provide for other behavior, but typically you'll
instead want to just set the ``media_type`` attribute on the class.
instead want to just set the :attr:`media_type` attribute on the class.
"""
return media_type_matches(content_type, self.media_type)
def parse(self, stream):
"""
Given a stream to read from, return the deserialized output.
Given a *stream* to read from, return the deserialized output.
Should return a 2-tuple of (data, files).
"""
raise NotImplementedError("BaseParser.parse() Must be overridden to be implemented.")
class JSONParser(BaseParser):
"""
Parses JSON-serialized data.
"""
media_type = 'application/json'
def parse(self, stream):
@ -74,11 +79,14 @@ class JSONParser(BaseParser):
class DataFlatener(object):
"""Utility object for flattening dictionaries of lists. Useful for "urlencoded" decoded data."""
"""
Utility object for flattening dictionaries of lists. Useful for "urlencoded" decoded data.
"""
# TODO: move me to utils ??
def flatten_data(self, data):
"""Given a data dictionary {<key>: <value_list>}, returns a flattened dictionary
with information provided by the method "is_a_list"."""
"""Given a *data* dictionary ``{<key>: <value_list>}``, returns a flattened dictionary
with information provided by the method :meth:`is_a_list`."""
flatdata = dict()
for key, val_list in data.items():
if self.is_a_list(key, val_list):
@ -93,15 +101,13 @@ class DataFlatener(object):
return flatdata
def is_a_list(self, key, val_list):
"""Returns True if the parameter with name *key* is expected to be a list, or False otherwise.
"""Returns :const:`True` if the parameter with name *key* is expected to be a list, or :const:`False` otherwise.
*val_list* which is the received value for parameter *key* can be used to guess the answer."""
return False
class PlainTextParser(BaseParser):
"""
Plain text parser.
Simply returns the content of the stream.
"""
media_type = 'text/plain'
@ -113,10 +119,10 @@ class PlainTextParser(BaseParser):
class FormParser(BaseParser, DataFlatener):
"""
The default parser for form data.
Return a dict containing a single value for each non-reserved parameter.
Returns a dict containing a single value for each non-reserved parameter.
In order to handle select multiple (and having possibly more than a single value for each parameter),
you can customize the output by subclassing the method 'is_a_list'."""
you can customize the output by subclassing the method :meth:`DataFlatener.is_a_list`."""
media_type = 'application/x-www-form-urlencoded'

View File

@ -36,7 +36,7 @@ class BasePermission(object):
def check_permission(self, auth):
"""
Should simply return, or raise an ErrorResponse.
Should simply return, or raise an :class:`response.ErrorResponse`.
"""
pass
@ -59,7 +59,7 @@ class IsAuthenticated(BasePermission):
if not user.is_authenticated():
raise _403_FORBIDDEN_RESPONSE
class IsAdminUser():
class IsAdminUser(BasePermission):
"""
Allows access only to admin users.
"""
@ -85,7 +85,7 @@ class PerUserThrottling(BasePermission):
"""
Rate throttling of requests on a per-user basis.
The rate is set by a 'throttle' attribute on the ``View`` class.
The rate (requests / seconds) is set by a :attr:`throttle` attribute on the ``View`` class.
The attribute is a two tuple of the form (number of requests, duration in seconds).
The user id will be used as a unique identifier if the user is authenticated.

View File

@ -36,8 +36,8 @@ __all__ = (
class BaseRenderer(object):
"""
All renderers must extend this class, set the media_type attribute,
and override the render() function.
All renderers must extend this class, set the :attr:`media_type` attribute,
and override the :meth:`render` method.
"""
media_type = None
@ -51,7 +51,7 @@ class BaseRenderer(object):
The requested media type is also passed to this method,
as it may contain parameters relevant to how the parser
should render the output.
EG: 'application/json; indent=4'
EG: ``application/json; indent=4``
By default render simply returns the output as-is.
Override this method to provide for other behavior.
@ -102,8 +102,8 @@ class TemplateRenderer(BaseRenderer):
A Base class provided for convenience.
Render the object simply by using the given template.
To create a template renderer, subclass this, and set
the ``media_type`` and ``template`` attributes
To create a template renderer, subclass this class, and set
the :attr:`media_type` and `:attr:template` attributes.
"""
media_type = None
template = None