<commit_before>import six

from .constants import OK, CREATED, ACCEPTED, NO_CONTENT
from .exceptions import MethodNotImplemented, Unauthorized
from .utils import json, lookup_data, MoreTypesJSONEncoder


class Resource(object):
    fields = None
    status_map = {
        'list': OK,
        'detail': OK,
        'create': CREATED,
        'update': ACCEPTED,
        'delete': NO_CONTENT,
        'update_list': ACCEPTED,
        'create_detail': CREATED,
        'delete_list': NO_CONTENT,
    }
    http_methods = {
        'list': {
            'GET': 'list',
            'POST': 'create',
            'PUT': 'update_list',
            'DELETE': 'delete_list',
        },
        'detail': {
            'GET': 'detail',
            'POST': 'create_detail',
            'PUT': 'update',
            'DELETE': 'delete',
        }
    }

    def __init__(self, *args, **kwargs):
        self.init_args = args
        self.init_kwargs = kwargs
        self.request = None
        self.data = None
        self.status = 200

    @classmethod
    def as_list(cls, *init_args, **init_kwargs):
        def _wrapper(request, *args, **kwargs):
            # Make a new instance so that no state potentially leaks between
            # instances.
            inst = cls(*init_args, **init_kwargs)
            inst.request = request
            return inst.handle('list', *args, **kwargs)

        return _wrapper

    @classmethod
    def as_detail(cls, *init_args, **init_kwargs):
        def _wrapper(request, *args, **kwargs):
            # Make a new instance so that no state potentially leaks between
            # instances.
            inst = cls(*init_args, **init_kwargs)
            inst.request = request
            return inst.handle('detail', *args, **kwargs)

        return _wrapper

    def request_method(self):
        # By default, Django-esque.
        return self.request.method.upper()

    def request_body(self):
        # By default, Django-esque.
        return self.request.body

    def build_response(self, data, status=200):
        # FIXME: Remove the Django.
        #        This should be plain old WSGI by default if possible
        # By default, Django-esque.
        from django.http import HttpResponse
        resp = HttpResponse(data, content_type='application/json')
        resp.status_code = status
        return resp

    def build_error(self, err):
        data = json.dumps({
            'error': six.text_type(err),
        })
        status = getattr(err, 'status', 500)
        return self.build_response(data, status=status)

    def is_debug(self):
        return False

    def handle(self, endpoint, *args, **kwargs):
        method = self.request_method()

        try:
            # Use ``.get()`` so we can also dodge potentially incorrect
            # ``endpoint`` errors as well.
            if not method in self.http_methods.get(endpoint, {}):
                raise MethodNotImplemented(
                    "Unsupported method '{0}' for {1} endpoint.".format(
                        method,
                        endpoint
                    )
                )

            deserialize_method = getattr(self, 'deserialize_{0}'.format(endpoint))
            self.data = deserialize_method()

            method = getattr(self, self.http_methods[endpoint][method])
            data = method(*args, **kwargs)
            if not self.is_authenticated():
                raise Unauthorized()

            serialize_method = getattr(self, 'serialize_{0}'.format(endpoint))
            serialized = serialize_method(data)
        except Exception as err:
            if self.is_debug():
                raise

            return self.build_error(err)

        return self.build_response(serialized, status=self.status_map.get(method, OK))

    def deserialize_list(self):
        body = self.request_body()

        if body:
            return json.loads(body)

        return []

    def deserialize_detail(self):
        body = self.request_body()

        if body:
            return json.loads(body)

        return {}

    def serialize_list(self, data):
        if data is None:
            return ''

        final_data = [self.prepare(item) for item in data]
        return json.dumps(final_data, cls=MoreTypesJSONEncoder)

    def serialize_detail(self, data):
        if data is None:
            return ''

        final_data = self.prepare(data)
        return json.dumps(final_data, cls=MoreTypesJSONEncoder)

    def prepare(self, data):
        result = {}

        for fieldname, lookup in self.fields.items():
            result[fieldname] = lookup_data(lookup, data)

        return result

    def is_authenticated(self):
        """

        By default, we only allow the safe ``GET`` methods. All others are
        denied.
        """
        if self.request_method() == 'GET':
            return True

        return False

    # Common methods the user should implement.

    def list(self, *args, **kwargs):
        raise MethodNotImplemented()

    def detail(self, *args, **kwargs):
        raise MethodNotImplemented()

    def create(self, *args, **kwargs):<commit_msg>Added wrapping of serialized lists for security.<commit_after>import six

from .constants import OK, CREATED, ACCEPTED, NO_CONTENT
from .exceptions import MethodNotImplemented, Unauthorized
from .utils import json, lookup_data, MoreTypesJSONEncoder


class Resource(object):
    fields = None
    status_map = {
        'list': OK,
        'detail': OK,
        'create': CREATED,
        'update': ACCEPTED,
        'delete': NO_CONTENT,
        'update_list': ACCEPTED,
        'create_detail': CREATED,
        'delete_list': NO_CONTENT,
    }
    http_methods = {
        'list': {
            'GET': 'list',
            'POST': 'create',
            'PUT': 'update_list',
            'DELETE': 'delete_list',
        },
        'detail': {
            'GET': 'detail',
            'POST': 'create_detail',
            'PUT': 'update',
            'DELETE': 'delete',
        }
    }

    def __init__(self, *args, **kwargs):
        self.init_args = args
        self.init_kwargs = kwargs
        self.request = None
        self.data = None
        self.status = 200

    @classmethod
    def as_list(cls, *init_args, **init_kwargs):
        def _wrapper(request, *args, **kwargs):
            # Make a new instance so that no state potentially leaks between
            # instances.
            inst = cls(*init_args, **init_kwargs)
            inst.request = request
            return inst.handle('list', *args, **kwargs)

        return _wrapper

    @classmethod
    def as_detail(cls, *init_args, **init_kwargs):
        def _wrapper(request, *args, **kwargs):
            # Make a new instance so that no state potentially leaks between
            # instances.
            inst = cls(*init_args, **init_kwargs)
            inst.request = request
            return inst.handle('detail', *args, **kwargs)

        return _wrapper

    def request_method(self):
        # By default, Django-esque.
        return self.request.method.upper()

    def request_body(self):
        # By default, Django-esque.
        return self.request.body

    def build_response(self, data, status=200):
        # FIXME: Remove the Django.
        #        This should be plain old WSGI by default if possible
        # By default, Django-esque.
        from django.http import HttpResponse
        resp = HttpResponse(data, content_type='application/json')
        resp.status_code = status
        return resp

    def build_error(self, err):
        data = json.dumps({
            'error': six.text_type(err),
        })
        status = getattr(err, 'status', 500)
        return self.build_response(data, status=status)

    def is_debug(self):
        return False

    def handle(self, endpoint, *args, **kwargs):
        method = self.request_method()

        try:
            # Use ``.get()`` so we can also dodge potentially incorrect
            # ``endpoint`` errors as well.
            if not method in self.http_methods.get(endpoint, {}):
                raise MethodNotImplemented(
                    "Unsupported method '{0}' for {1} endpoint.".format(
                        method,
                        endpoint
                    )
                )

            deserialize_method = getattr(self, 'deserialize_{0}'.format(endpoint))
            self.data = deserialize_method()

            method = getattr(self, self.http_methods[endpoint][method])
            data = method(*args, **kwargs)
            if not self.is_authenticated():
                raise Unauthorized()

            serialize_method = getattr(self, 'serialize_{0}'.format(endpoint))
            serialized = serialize_method(data)
        except Exception as err:
            if self.is_debug():
                raise

            return self.build_error(err)

        return self.build_response(serialized, status=self.status_map.get(method, OK))

    def deserialize_list(self):
        body = self.request_body()

        if body:
            return json.loads(body)

        return []

    def deserialize_detail(self):
        body = self.request_body()

        if body:
            return json.loads(body)

        return {}

    def serialize_list(self, data):
        if data is None:
            return ''

        final_data = [self.prepare(item) for item in data]
        final_data = self.wrap_list_response(prepped_data)
        return json.dumps(final_data, cls=MoreTypesJSONEncoder)

    def serialize_detail(self, data):
        if data is None:
            return ''

        final_data = self.prepare(data)
        return json.dumps(final_data, cls=MoreTypesJSONEncoder)

    def prepare(self, data):
        result = {}

        for fieldname, lookup in self.fields.items():
            result[fieldname] = lookup_data(lookup, data)

        return result

    def wrap_list_response(self, data):
        """
        For security in JSON responses, it's better to wrap the result in an
        ``object``. See http://haacked.com/archive/2009/06/25/json-hijacking.aspx/
        & similar for details.

        Overridable to allow for modifying the key names, adding data (or just
        insecurely return a plain old list if that's your thing).
        """
        return {
            "objects": data
        }

    def is_authenticated(self):
        """

        By default, we only allow the safe ``GET`` methods. All others are
        denied.
        """
        if self.request_method() == 'GET':
            return True

        return False

    # Common methods the user should implement.

    def list(self, *args, **kwargs):
        raise MethodNotImplemented()

    def detail(self, *args, **kwargs):
        raise MethodNotImplemented()

    def create(self, *args, **kwargs):
{'fraction_non_alphanumeric': 0.10363552692130695, 'fraction_numerical': 0.003129314312011045, 'mean_word_length': 1.9399350649350648, 'pattern_counts': {'":': 1, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1739794', 'n_tokens_mistral': 3034, 'n_tokens_neox': 2921, 'n_words': 810}