<commit_before># -*- coding: utf-8 -*-

"""
    eve.render
    ~~~~~~~~~~

    Implements proper, automated rendering for Eve responses.

    :copyright: (c) 2012 by <NAME>.
    :license: BSD, see LICENSE for more details.
"""

import datetime
import time
import simplejson as json
from flask import make_response, request
from bson.objectid import ObjectId
from eve.utils import date_to_str, config

# mapping between supported mime types and render functions.
_MIME_TYPES = [{'mime': ('application/json',), 'renderer': 'render_json'},
               {'mime': ('application/xml', 'text/xml', 'application/x-xml',),
                'renderer': 'render_xml'}]
_DEFAULT_MIME = 'application/json'


def send_response(resource, dct, last_modified=None, etag=None, status=200):
    """ Prepares the response object according to the client request and
    available renderers, making sure that all accessory directives (caching,
    etag, last-modified) are present.

    :param resource: the resource involved.
    :param dct: the dict that should be sent back as a response.
    :param last_modified: Last-Modified header value.
    :param etag: ETag header value.
    :param status: response status.
    """
    # obtain the best match between client's request and available mime types,
    # along with the corresponding render function.
    mime, renderer = _best_mime()

    # invoke the render function and obtain the corresponding rendered item
    rendered = globals()[renderer](**dct)

    # build the main wsgi rensponse object
    resp = make_response(rendered, status)
    resp.mimetype = mime

    # cache directives
    if request.method == 'GET':
        if resource:
            cache_control = config.DOMAIN[resource]['cache_control']
            expires = config.DOMAIN[resource]['cache_expires']
        else:
            cache_control = config.CACHE_CONTROL
            expires = config.CACHE_EXPIRES
        if cache_control:
            resp.headers.add('Cache-Control', cache_control)
        if expires:
            resp.expires = time.time() + expires

    # etag and last-modified
    if etag:
        resp.headers.add('ETag', etag)
    if last_modified:
        resp.headers.add('Last-Modified', date_to_str(last_modified))

    return resp


def _best_mime():
    """ Returns the best match between the requested mime type and the
    ones supported by Eve. Along with the mime, also the corresponding
    render function is returns.
    """
    supported = list()
    renders = dict()
    for mime in _MIME_TYPES:
        for mime_type in mime['mime']:
            supported.append(mime_type)
            renders[mime_type] = mime['renderer']
    best_match = request.accept_mimetypes.best_match(supported) or \
        _DEFAULT_MIME
    return best_match, renders[best_match]


class APIEncoder(json.JSONEncoder):
    """ Propretary JSONEconder subclass used by the json render function.
    This is needed to address the encoding of special values.
    """
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            # convert any datetime to RFC 1123 format
            return date_to_str(obj)
        elif isinstance(obj, (datetime.time, datetime.date)):
            # should not happen since the only supported date-like format
            # supported at dmain schema level is 'datetime' .
            return obj.isoformat()
        elif isinstance(obj, ObjectId):
            # BSON/Mongo ObjectId is rendered as a string
            return str(obj)
        return json.JSONEncoder.default(self, obj)


def render_json(**data):
    """ JSON render function
    """
    return json.dumps(data, cls=APIEncoder)


def render_xml(**d):
    """ XML render function. This could surely use some further tinkering.
    """
    xml = ''
    for k, v in d.items():
        if isinstance(v, datetime.datetime):
            v = date_to_str(v)
        elif isinstance(v, (datetime.time, datetime.date)):
            v = v.isoformat()
        if type(v) is dict:
            xml += "<%s>" % (k.rstrip('s'))
            xml += render_xml(**v)
            xml += "</%s>" % (k.rstrip('s'))
        else:
            original_list = False
            if type(v) is not list:
                v = [v]
            else:
                original_list = True
                xml += "<%s>" % k
            for value in v:
                if type(value) is dict:
                    xml += "<%s>" % (k.rstrip('s'))
                    xml += render_xml(**value)
                    xml += "</%s>" % (k.rstrip('s'))
                else:
                    xml += "<%s>%s</%s>" % (str(k.rstrip('s')), value,
                                            str(k.rstrip('s')))
            if original_list:
                xml += "</%s>" % k
    return xml<commit_msg>In order to properly validate, the XML render now adds a 'response' root node to the default payload.<commit_after># -*- coding: utf-8 -*-

"""
    eve.render
    ~~~~~~~~~~

    Implements proper, automated rendering for Eve responses.

    :copyright: (c) 2012 by <NAME>.
    :license: BSD, see LICENSE for more details.
"""

import datetime
import time
import simplejson as json
from flask import make_response, request
from bson.objectid import ObjectId
from eve.utils import date_to_str, config

# mapping between supported mime types and render functions.
_MIME_TYPES = [{'mime': ('application/json',), 'renderer': 'render_json'},
               {'mime': ('application/xml', 'text/xml', 'application/x-xml',),
                'renderer': 'render_xml'}]
_DEFAULT_MIME = 'application/json'


def send_response(resource, dct, last_modified=None, etag=None, status=200):
    """ Prepares the response object according to the client request and
    available renderers, making sure that all accessory directives (caching,
    etag, last-modified) are present.

    :param resource: the resource involved.
    :param dct: the dict that should be sent back as a response.
    :param last_modified: Last-Modified header value.
    :param etag: ETag header value.
    :param status: response status.
    """
    # obtain the best match between client's request and available mime types,
    # along with the corresponding render function.
    mime, renderer = _best_mime()

    # invoke the render function and obtain the corresponding rendered item
    rendered = globals()[renderer](**dct)

    # build the main wsgi rensponse object
    resp = make_response(rendered, status)
    resp.mimetype = mime

    # cache directives
    if request.method == 'GET':
        if resource:
            cache_control = config.DOMAIN[resource]['cache_control']
            expires = config.DOMAIN[resource]['cache_expires']
        else:
            cache_control = config.CACHE_CONTROL
            expires = config.CACHE_EXPIRES
        if cache_control:
            resp.headers.add('Cache-Control', cache_control)
        if expires:
            resp.expires = time.time() + expires

    # etag and last-modified
    if etag:
        resp.headers.add('ETag', etag)
    if last_modified:
        resp.headers.add('Last-Modified', date_to_str(last_modified))

    return resp


def _best_mime():
    """ Returns the best match between the requested mime type and the
    ones supported by Eve. Along with the mime, also the corresponding
    render function is returns.
    """
    supported = list()
    renders = dict()
    for mime in _MIME_TYPES:
        for mime_type in mime['mime']:
            supported.append(mime_type)
            renders[mime_type] = mime['renderer']
    best_match = request.accept_mimetypes.best_match(supported) or \
        _DEFAULT_MIME
    return best_match, renders[best_match]


class APIEncoder(json.JSONEncoder):
    """ Propretary JSONEconder subclass used by the json render function.
    This is needed to address the encoding of special values.
    """
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            # convert any datetime to RFC 1123 format
            return date_to_str(obj)
        elif isinstance(obj, (datetime.time, datetime.date)):
            # should not happen since the only supported date-like format
            # supported at dmain schema level is 'datetime' .
            return obj.isoformat()
        elif isinstance(obj, ObjectId):
            # BSON/Mongo ObjectId is rendered as a string
            return str(obj)
        return json.JSONEncoder.default(self, obj)


def render_json(**data):
    """ JSON render function
    """
    return json.dumps(data, cls=APIEncoder)


def render_xml(**data):
    """ XML render function.

    :param data: the data stream to be rendered as xml.

    .. versionchanged:: 0.0.3
       Addition of 'response' root node, since the payload does not include it
       anymore.
    """
    # Assumes list names are plurals.
    def to_xml(d):
        xml = ''
        for k, v in d.items():
            if isinstance(v, datetime.datetime):
                v = date_to_str(v)
            elif isinstance(v, (datetime.time, datetime.date)):
                v = v.isoformat()
            else:
                if isinstance(v, list):
                    original_list = True
                    xml += "<%s>" % k
                else:
                    original_list = False
                    v = [v]
                for value in v:
                    if isinstance(value, dict):
                        xml += "<%s>" % (k.rstrip('s'))
                        xml += to_xml(value)
                        xml += "</%s>" % (k.rstrip('s'))
                    else:
                        if original_list:
                            xml += "<%s>%s</%s>" % (str(k.rstrip('s')), value,
                                                    str(k.rstrip('s')))
                        else:
                            xml += "<%s>%s</%s>" % (k, value, k)
                if original_list:
                    xml += "</%s>" % k
        return xml
    return to_xml({'response': data})
{'fraction_non_alphanumeric': 0.10605757696921231, 'fraction_numerical': 0.0026989204318272693, 'mean_word_length': 2.1731684110371075, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 36}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '173668', 'n_tokens_mistral': 2820, 'n_tokens_neox': 2711, 'n_words': 885}