diff options
Diffstat (limited to 'rest_framework/utils')
| -rw-r--r-- | rest_framework/utils/__init__.py | 100 | ||||
| -rw-r--r-- | rest_framework/utils/breadcrumbs.py | 36 | ||||
| -rw-r--r-- | rest_framework/utils/encoders.py | 13 | ||||
| -rw-r--r-- | rest_framework/utils/formatting.py | 80 | ||||
| -rw-r--r-- | rest_framework/utils/mediatypes.py | 5 |
5 files changed, 117 insertions, 117 deletions
diff --git a/rest_framework/utils/__init__.py b/rest_framework/utils/__init__.py index 84fcb5db..e69de29b 100644 --- a/rest_framework/utils/__init__.py +++ b/rest_framework/utils/__init__.py @@ -1,100 +0,0 @@ -from django.utils.encoding import smart_unicode -from django.utils.xmlutils import SimplerXMLGenerator -from rest_framework.compat import StringIO -import re -import xml.etree.ElementTree as ET - - -# From xml2dict -class XML2Dict(object): - - def __init__(self): - pass - - def _parse_node(self, node): - node_tree = {} - # Save attrs and text, hope there will not be a child with same name - if node.text: - node_tree = node.text - for (k, v) in node.attrib.items(): - k, v = self._namespace_split(k, v) - node_tree[k] = v - #Save childrens - for child in node.getchildren(): - tag, tree = self._namespace_split(child.tag, self._parse_node(child)) - if tag not in node_tree: # the first time, so store it in dict - node_tree[tag] = tree - continue - old = node_tree[tag] - if not isinstance(old, list): - node_tree.pop(tag) - node_tree[tag] = [old] # multi times, so change old dict to a list - node_tree[tag].append(tree) # add the new one - - return node_tree - - def _namespace_split(self, tag, value): - """ - Split the tag '{http://cs.sfsu.edu/csc867/myscheduler}patients' - ns = http://cs.sfsu.edu/csc867/myscheduler - name = patients - """ - result = re.compile("\{(.*)\}(.*)").search(tag) - if result: - value.namespace, tag = result.groups() - return (tag, value) - - def parse(self, file): - """parse a xml file to a dict""" - f = open(file, 'r') - return self.fromstring(f.read()) - - def fromstring(self, s): - """parse a string""" - t = ET.fromstring(s) - unused_root_tag, root_tree = self._namespace_split(t.tag, self._parse_node(t)) - return root_tree - - -def xml2dict(input): - return XML2Dict().fromstring(input) - - -# Piston: -class XMLRenderer(): - def _to_xml(self, xml, data): - if isinstance(data, (list, tuple)): - for item in data: - xml.startElement("list-item", {}) - self._to_xml(xml, item) - xml.endElement("list-item") - - elif isinstance(data, dict): - for key, value in data.iteritems(): - xml.startElement(key, {}) - self._to_xml(xml, value) - xml.endElement(key) - - elif data is None: - # Don't output any value - pass - - else: - xml.characters(smart_unicode(data)) - - def dict2xml(self, data): - stream = StringIO.StringIO() - - xml = SimplerXMLGenerator(stream, "utf-8") - xml.startDocument() - xml.startElement("root", {}) - - self._to_xml(xml, data) - - xml.endElement("root") - xml.endDocument() - return stream.getvalue() - - -def dict2xml(input): - return XMLRenderer().dict2xml(input) diff --git a/rest_framework/utils/breadcrumbs.py b/rest_framework/utils/breadcrumbs.py index 80e39d46..d51374b0 100644 --- a/rest_framework/utils/breadcrumbs.py +++ b/rest_framework/utils/breadcrumbs.py @@ -1,25 +1,37 @@ +from __future__ import unicode_literals from django.core.urlresolvers import resolve, get_script_prefix +from rest_framework.utils.formatting import get_view_name def get_breadcrumbs(url): - """Given a url returns a list of breadcrumbs, which are each a tuple of (name, url).""" + """ + Given a url returns a list of breadcrumbs, which are each a + tuple of (name, url). + """ from rest_framework.views import APIView def breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen): - """Add tuples of (name, url) to the breadcrumbs list, progressively chomping off parts of the url.""" + """ + Add tuples of (name, url) to the breadcrumbs list, + progressively chomping off parts of the url. + """ try: (view, unused_args, unused_kwargs) = resolve(url) except Exception: pass else: - # Check if this is a REST framework view, and if so add it to the breadcrumbs - if isinstance(getattr(view, 'cls_instance', None), APIView): + # Check if this is a REST framework view, + # and if so add it to the breadcrumbs + cls = getattr(view, 'cls', None) + if cls is not None and issubclass(cls, APIView): # Don't list the same view twice in a row. # Probably an optional trailing slash. if not seen or seen[-1] != view: - breadcrumbs_list.insert(0, (view.cls_instance.get_name(), prefix + url)) + suffix = getattr(view, 'suffix', None) + name = get_view_name(view.cls, suffix) + breadcrumbs_list.insert(0, (name, prefix + url)) seen.append(view) if url == '': @@ -27,11 +39,15 @@ def get_breadcrumbs(url): return breadcrumbs_list elif url.endswith('/'): - # Drop trailing slash off the end and continue to try to resolve more breadcrumbs - return breadcrumbs_recursive(url.rstrip('/'), breadcrumbs_list, prefix, seen) - - # Drop trailing non-slash off the end and continue to try to resolve more breadcrumbs - return breadcrumbs_recursive(url[:url.rfind('/') + 1], breadcrumbs_list, prefix, seen) + # Drop trailing slash off the end and continue to try to + # resolve more breadcrumbs + url = url.rstrip('/') + return breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen) + + # Drop trailing non-slash off the end and continue to try to + # resolve more breadcrumbs + url = url[:url.rfind('/') + 1] + return breadcrumbs_recursive(url, breadcrumbs_list, prefix, seen) prefix = get_script_prefix().rstrip('/') url = url[len(prefix):] diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index 2d1fb353..b6de18a8 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -1,18 +1,19 @@ """ Helper classes for parsers. """ -import datetime -import decimal -import types -from django.utils import simplejson as json +from __future__ import unicode_literals from django.utils.datastructures import SortedDict from rest_framework.compat import timezone from rest_framework.serializers import DictWithMetadata, SortedDictWithMetadata +import datetime +import decimal +import types +import json class JSONEncoder(json.JSONEncoder): """ - JSONEncoder subclass that knows how to encode date/time, + JSONEncoder subclass that knows how to encode date/time/timedelta, decimal types, and generators. """ def default(self, o): @@ -34,6 +35,8 @@ class JSONEncoder(json.JSONEncoder): if o.microsecond: r = r[:12] return r + elif isinstance(o, datetime.timedelta): + return str(o.total_seconds()) elif isinstance(o, decimal.Decimal): return str(o) elif hasattr(o, '__iter__'): diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py new file mode 100644 index 00000000..ebadb3a6 --- /dev/null +++ b/rest_framework/utils/formatting.py @@ -0,0 +1,80 @@ +""" +Utility functions to return a formatted name and description for a given view. +""" +from __future__ import unicode_literals + +from django.utils.html import escape +from django.utils.safestring import mark_safe +from rest_framework.compat import apply_markdown +import re + + +def _remove_trailing_string(content, trailing): + """ + Strip trailing component `trailing` from `content` if it exists. + Used when generating names from view classes. + """ + if content.endswith(trailing) and content != trailing: + return content[:-len(trailing)] + return content + + +def _remove_leading_indent(content): + """ + Remove leading indent from a block of text. + Used when generating descriptions from docstrings. + """ + whitespace_counts = [len(line) - len(line.lstrip(' ')) + for line in content.splitlines()[1:] if line.lstrip()] + + # unindent the content if needed + if whitespace_counts: + whitespace_pattern = '^' + (' ' * min(whitespace_counts)) + content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content) + content = content.strip('\n') + return content + + +def _camelcase_to_spaces(content): + """ + Translate 'CamelCaseNames' to 'Camel Case Names'. + Used when generating names from view classes. + """ + camelcase_boundry = '(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))' + content = re.sub(camelcase_boundry, ' \\1', content).strip() + return ' '.join(content.split('_')).title() + + +def get_view_name(cls, suffix=None): + """ + Return a formatted name for an `APIView` class or `@api_view` function. + """ + name = cls.__name__ + name = _remove_trailing_string(name, 'View') + name = _remove_trailing_string(name, 'ViewSet') + name = _camelcase_to_spaces(name) + if suffix: + name += ' ' + suffix + return name + + +def get_view_description(cls, html=False): + """ + Return a description for an `APIView` class or `@api_view` function. + """ + description = cls.__doc__ or '' + description = _remove_leading_indent(description) + if html: + return markup_description(description) + return description + + +def markup_description(description): + """ + Apply HTML markup to the given description. + """ + if apply_markdown: + description = apply_markdown(description) + else: + description = escape(description).replace('\n', '<br />') + return mark_safe(description) diff --git a/rest_framework/utils/mediatypes.py b/rest_framework/utils/mediatypes.py index ee7f3a54..c09c2933 100644 --- a/rest_framework/utils/mediatypes.py +++ b/rest_framework/utils/mediatypes.py @@ -3,8 +3,9 @@ Handling of media types, as found in HTTP Content-Type and Accept headers. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7 """ - +from __future__ import unicode_literals from django.http.multipartparser import parse_header +from rest_framework import HTTP_HEADER_ENCODING def media_type_matches(lhs, rhs): @@ -47,7 +48,7 @@ class _MediaType(object): if media_type_str is None: media_type_str = '' self.orig = media_type_str - self.full_type, self.params = parse_header(media_type_str) + self.full_type, self.params = parse_header(media_type_str.encode(HTTP_HEADER_ENCODING)) self.main_type, sep, self.sub_type = self.full_type.partition('/') def match(self, other): |
