<commit_before>import re
import os
import venusian
import logging

from inspect import getmembers
from collections import namedtuple, defaultdict

from pyramid.httpexceptions import (
    HTTPBadRequest,
    HTTPInternalServerError,
    HTTPNoContent,
)
from pyramid.interfaces import IExceptionResponse

from .apidef import IRamlApiDefinition
from .utils import prepare_json_body, render_view

log = logging.getLogger(__name__)

DEFAULT_METHOD_MAP = {
    'get': 200,
    'post': 200,
    'put': 201,
    'delete': 204,
    'options': 200,
    'patch': 200,
}

MethodRestConfig = namedtuple('MethodRestConfig', [
    'http_method',
    'permission',
    'returns'
])

class api_method(object):

    def __init__(self, http_method, permission=None, returns=None):
        """Configure a resource method corresponding with a RAML resource path

        This decorator must be used to declare REST resources.

        :param http_method: The HTTP method this method maps to.

        :param permission: Permission for this method.

        :param returns: A custom HTTP code to return in case of success.

            Configure the HTTP code to return when the method call was successful.
            Per default the codes are expected to match the configured HTTP method:
                - GET/POST/PATCH: 200
                - PUT: 201
                - DELETE: 204

        """
        self.http_method = http_method
        self.permission = permission
        self.returns = returns if returns is not None else DEFAULT_METHOD_MAP[self.http_method]

    def __call__(self, method):
        method._rest_config = MethodRestConfig(self.http_method,
                self.permission,
                self.returns)
        return method

class api_service(object):
    """Configures a resource by its REST path.

    This decorator configures a class as a REST resource. All endpoints
    must be defined in a RAML file.
    """

    def __init__(self, resource_path):
        log.debug("Resource path: {}".format(resource_path))
        self.resource_path = resource_path
        self.resources = []

    def callback(self, scanner, name, cls):
        config = scanner.config.with_package(self.module)
        apidef = config.registry.queryUtility(IRamlApiDefinition)
        self.create_route(config)
        log.debug("registered routes with base route '{}'".format(apidef.base_path))
        self.create_views(config)

    def create_route(self, config):
        log.debug("Creating route for {}".format(self.resource_path))
        supported_methods = []
        apidef = config.registry.queryUtility(IRamlApiDefinition)
        route_name = None

        # Find all methods for this resource path
        for resource in apidef.get_resources(self.resource_path):
            if route_name is None:
                path = self.resource_path
                if apidef.base_path:
                    path = "{}{}".format(apidef.base_path, path)
                route_name = "{}-{}".format(resource.display_name, path)

            method = resource.method.upper()
            self.resources.append((route_name, method, resource, None))
            supported_methods.append(method)

        # Add one route for all the methods at this resource path
        if supported_methods:
            log.debug("Registering route with path {}".format(path))
            config.add_route(route_name, path, factory=self.cls)
            # add a default OPTIONS view if none was defined by the resource
            opts_meth = 'OPTIONS'
            if opts_meth not in supported_methods:
                methods = supported_methods + [opts_meth]
                self.resources.append((route_name, 'OPTIONS', resource, create_options_view(methods)))

    def create_views(self, config):
        for (route_name, method, resource, default_view) in self.resources:
            log.debug("Creating view {} {}".format(route_name, method))
            if default_view:
                config.add_view(default_view,
                    route_name=route_name,
                    request_method=method)
            else:
                (view, permission) = self.create_view(resource)
                log.debug("Registering view {} for route name '{}', resource '{}', method '{}'".format(view, route_name, resource, method))
                config.add_view(view,
                        route_name=route_name,
                        request_method=method,
                        permission=permission)

    def __call__(self, cls):
        self.cls = cls
        info = venusian.attach(cls, self.callback, 'pyramid', depth=1)
        self.module = info.module
        return cls

    def create_view(self, resource):
        (meth, cfg) = self.get_service_class_method(resource)
        log.debug("Got method {} for resource {}".format(meth, resource))
        if not meth:
            raise ValueError("Could not find a method in class {} suitable for resource {}.".format(self.cls, resource))
        def view(context, request):
            required_params = [context]
            optional_params = dict()
            # URI parameters have the highest prio
            if resource.uri_params:
                for param in resource.uri_params:
                    param_value = request.matchdict[param.name]
                    validate_param(param, param_value)
                    # pyramid router makes sure the URI params are all
                    # set, otherwise the view isn't called all, because
                    # a NotFound error is triggered before the request
                    # can be routed to this view
                    required_params.append(param_value)
            # If there's a body defined - include it before traits or query params
            if resource.body:
                required_params.append(prepare_json_body(request, resource.body))
            # FIXME: handle traits
            #for (name, trait) in self.apidef.get_resource_traits(resource).items():
            if resource.query_params:
                for param in resource.query_params:
                    param_value = request.params.get(param.name, param.default)
                    if param_value:
                        validate_param(param, param_value)
                    # query params are always named (i.e. not positional)
                    # so they effectively become keyword agruments in a
                    # method call, we just make sure they are present
                    # in the request if marked as 'required'
                    if param.required and param.name not in request.params:
                        raise HTTPBadRequest("{} ({}) is required".format(param.name, param.type))
                    else:
                        optional_params[param.name] = param_value
            result = meth(*required_params, **optional_params)
            return render_view(request, result, cfg.returns)

        return (view, cfg.permission)

    def get_service_class_method(self, resource):
        rel_path = resource.path[len(self.resource_path):]
        log.debug("Relative path for {}: '{}'".format(resource, rel_path))
        http_method = resource.method.lower()
        for (name, member) in getmembers(self.cls):
            if not hasattr(member, '_rest_config'):
                continue
            cfg = member._rest_config
            if (cfg.http_method.lower() == http_method) and callable(member):
                return (member, cfg)
        return (None, None)

def create_options_view(supported_methods):
    def view(context, request):
        response = HTTPNoContent()
        response.headers['Access-Control-Allow-Methods'] =\
            ', '.join(supported_methods)
        return response
    return view

def includeme(config):
    """Configure basic RAML REST settings for a Pyramid application.

    You should not call this function directly, but use
    :py:func:`pyramid.config.Configurator.include` to initialise
    the RAML routing.

    .. code-block:: python
       :linenos:
       config = Configurator()
       config.include('pyramlson')
    """
    from pyramlson.apidef import RamlApiDefinition, IRamlApiDefinition
    settings = config.registry.settings
    settings['pyramlson.debug'] = \
            settings.get('debug_all') or \
            settings.get('pyramid.debug_all') or \
            settings.get('pyramlson.debug')
    config.add_view('pyramlson.error.generic', context=Exception, renderer='json')
    config.add_view('pyramlson.error.http_error', context=IExceptionResponse, renderer='json')
    config.add_notfound_view('pyramlson.error.notfound', renderer='json')
    config.add_forbidden_view('pyramlson.error.forbidden', renderer='json')

    if 'pyramlson.apidef_path' not in settings:
        raise ValueError("Cannot create RamlApiDefinition without a RAML file.")
    apidef = RamlApiDefinition(settings['pyramlson.apidef_path'])
    config.registry.registerUtility(apidef, IRamlApiDefinition)

def validate_param(param, value):
    # FIXME: add more checks
    if param.type == 'integer':
        try:
            int(value)
        except ValueError as e:
            raise HTTPBadRequest("Malformed parameter '{}', expected integer, got '{}'".format(param.name, value))
    if param.type == 'string':
        if param.enum and value not in param.enum:
            raise HTTPBadRequest("Malformed parameter '{}', expected one of {}, got '{}'".format(param.name, ', '.join(param.enum), value))
        if param.pattern:
            result = re.search(param.pattern, value)
            if not result:
                raise HTTPBadRequest("Malformed parameter '{}', expected pattern {}, got '{}'".format(param.name, param.pattern, value))

__all__ = ['api_method', 'api_service']
<commit_msg>Add max_/min_length validation for parameters<commit_after>import re
import os
import venusian
import logging

from inspect import getmembers
from collections import namedtuple, defaultdict

from pyramid.httpexceptions import (
    HTTPBadRequest,
    HTTPInternalServerError,
    HTTPNoContent,
)
from pyramid.interfaces import IExceptionResponse

from .apidef import IRamlApiDefinition
from .utils import prepare_json_body, render_view

log = logging.getLogger(__name__)

DEFAULT_METHOD_MAP = {
    'get': 200,
    'post': 200,
    'put': 201,
    'delete': 204,
    'options': 200,
    'patch': 200,
}

MethodRestConfig = namedtuple('MethodRestConfig', [
    'http_method',
    'permission',
    'returns'
])

MARKER = object()

class api_method(object):

    def __init__(self, http_method, permission=None, returns=None):
        """Configure a resource method corresponding with a RAML resource path

        This decorator must be used to declare REST resources.

        :param http_method: The HTTP method this method maps to.

        :param permission: Permission for this method.

        :param returns: A custom HTTP code to return in case of success.

            Configure the HTTP code to return when the method call was successful.
            Per default the codes are expected to match the configured HTTP method:
                - GET/POST/PATCH: 200
                - PUT: 201
                - DELETE: 204

        """
        self.http_method = http_method
        self.permission = permission
        self.returns = returns if returns is not None else DEFAULT_METHOD_MAP[self.http_method]

    def __call__(self, method):
        method._rest_config = MethodRestConfig(self.http_method,
                self.permission,
                self.returns)
        return method

class api_service(object):
    """Configures a resource by its REST path.

    This decorator configures a class as a REST resource. All endpoints
    must be defined in a RAML file.
    """

    def __init__(self, resource_path):
        log.debug("Resource path: {}".format(resource_path))
        self.resource_path = resource_path
        self.resources = []

    def callback(self, scanner, name, cls):
        config = scanner.config.with_package(self.module)
        apidef = config.registry.queryUtility(IRamlApiDefinition)
        self.create_route(config)
        log.debug("registered routes with base route '{}'".format(apidef.base_path))
        self.create_views(config)

    def create_route(self, config):
        log.debug("Creating route for {}".format(self.resource_path))
        supported_methods = []
        apidef = config.registry.queryUtility(IRamlApiDefinition)
        route_name = None

        # Find all methods for this resource path
        for resource in apidef.get_resources(self.resource_path):
            if route_name is None:
                path = self.resource_path
                if apidef.base_path:
                    path = "{}{}".format(apidef.base_path, path)
                route_name = "{}-{}".format(resource.display_name, path)

            method = resource.method.upper()
            self.resources.append((route_name, method, resource, None))
            supported_methods.append(method)

        # Add one route for all the methods at this resource path
        if supported_methods:
            log.debug("Registering route with path {}".format(path))
            config.add_route(route_name, path, factory=self.cls)
            # add a default OPTIONS view if none was defined by the resource
            opts_meth = 'OPTIONS'
            if opts_meth not in supported_methods:
                methods = supported_methods + [opts_meth]
                self.resources.append((route_name, 'OPTIONS', resource, create_options_view(methods)))

    def create_views(self, config):
        for (route_name, method, resource, default_view) in self.resources:
            log.debug("Creating view {} {}".format(route_name, method))
            if default_view:
                config.add_view(default_view,
                    route_name=route_name,
                    request_method=method)
            else:
                (view, permission) = self.create_view(resource)
                log.debug("Registering view {} for route name '{}', resource '{}', method '{}'".format(view, route_name, resource, method))
                config.add_view(view,
                        route_name=route_name,
                        request_method=method,
                        permission=permission)

    def __call__(self, cls):
        self.cls = cls
        info = venusian.attach(cls, self.callback, 'pyramid', depth=1)
        self.module = info.module
        return cls

    def create_view(self, resource):
        (meth, cfg) = self.get_service_class_method(resource)
        log.debug("Got method {} for resource {}".format(meth, resource))
        if not meth:
            raise ValueError("Could not find a method in class {} suitable for resource {}.".format(self.cls, resource))
        def view(context, request):
            required_params = [context]
            optional_params = dict()
            # URI parameters have the highest prio
            if resource.uri_params:
                for param in resource.uri_params:
                    param_value = request.matchdict[param.name]
                    validate_param(param, param_value)
                    # pyramid router makes sure the URI params are all
                    # set, otherwise the view isn't called all, because
                    # a NotFound error is triggered before the request
                    # can be routed to this view
                    required_params.append(param_value)
            # If there's a body defined - include it before traits or query params
            if resource.body:
                required_params.append(prepare_json_body(request, resource.body))
            if resource.query_params:
                for param in resource.query_params:
                    param_value = request.params.get(param.name, MARKER)
                    if param_value is not MARKER:
                        validate_param(param, param_value)
                    else:
                        param_value = param.default
                    # query params are always named (i.e. not positional)
                    # so they effectively become keyword agruments in a
                    # method call, we just make sure they are present
                    # in the request if marked as 'required'
                    if param.required and param.name not in request.params:
                        raise HTTPBadRequest("{} ({}) is required".format(param.name, param.type))
                    else:
                        optional_params[param.name] = param_value
            result = meth(*required_params, **optional_params)
            return render_view(request, result, cfg.returns)

        return (view, cfg.permission)

    def get_service_class_method(self, resource):
        rel_path = resource.path[len(self.resource_path):]
        log.debug("Relative path for {}: '{}'".format(resource, rel_path))
        http_method = resource.method.lower()
        for (name, member) in getmembers(self.cls):
            if not hasattr(member, '_rest_config'):
                continue
            cfg = member._rest_config
            if (cfg.http_method.lower() == http_method) and callable(member):
                return (member, cfg)
        return (None, None)

def create_options_view(supported_methods):
    def view(context, request):
        response = HTTPNoContent()
        response.headers['Access-Control-Allow-Methods'] =\
            ', '.join(supported_methods)
        return response
    return view

def includeme(config):
    """Configure basic RAML REST settings for a Pyramid application.

    You should not call this function directly, but use
    :py:func:`pyramid.config.Configurator.include` to initialise
    the RAML routing.

    .. code-block:: python
       :linenos:
       config = Configurator()
       config.include('pyramlson')
    """
    from pyramlson.apidef import RamlApiDefinition, IRamlApiDefinition
    settings = config.registry.settings
    settings['pyramlson.debug'] = \
            settings.get('debug_all') or \
            settings.get('pyramid.debug_all') or \
            settings.get('pyramlson.debug')
    config.add_view('pyramlson.error.generic', context=Exception, renderer='json')
    config.add_view('pyramlson.error.http_error', context=IExceptionResponse, renderer='json')
    config.add_notfound_view('pyramlson.error.notfound', renderer='json')
    config.add_forbidden_view('pyramlson.error.forbidden', renderer='json')

    if 'pyramlson.apidef_path' not in settings:
        raise ValueError("Cannot create RamlApiDefinition without a RAML file.")
    apidef = RamlApiDefinition(settings['pyramlson.apidef_path'])
    config.registry.registerUtility(apidef, IRamlApiDefinition)

def validate_param(param, value):
    # FIXME: add more checks
    if param.type == 'integer':
        try:
            int(value)
        except ValueError as e:
            raise HTTPBadRequest("Malformed parameter '{}', expected integer, got '{}'".format(param.name, value))
    if param.type == 'string':
        if param.enum and value not in param.enum:
            raise HTTPBadRequest("Malformed parameter '{}', expected one of {}, got '{}'".format(param.name, ', '.join(param.enum), value))
        if param.pattern:
            result = re.search(param.pattern, value)
            if not result:
                raise HTTPBadRequest("Malformed parameter '{}', expected pattern {}, got '{}'".format(param.name, param.pattern, value))
        if param.min_length and len(value) < param.min_length:
            msg = "Malformed parameter '{}', expected minimum length is {}, got {}"
            raise HTTPBadRequest(msg.format(
                param.name,
                param.min_length,
                len(value)
            ))
        if param.max_length and len(value) > param.max_length:
            msg = "Malformed parameter '{}', expected maximum length is {}, got {}"
            raise HTTPBadRequest(msg.format(
                param.name,
                param.max_length,
                len(value)
            ))
__all__ = ['api_method', 'api_service']
<|endoftext|>


<commit_before>import ctypes

#Import low level api
from _spotify import session as _session

#Import general classes from the high level module
import spotify

#Also import this one's siblings
from spotify import user, playlistcontainer

#Threading stuff
import threading

#Decorators
from spotify.utils.decorators import synchronized

#classes
class Session:
    api_version = 7
    _manager = None

    #Instance variable to avoid them getting garbage collected
    _callbacks = None

    _user_callbacks = None
    _metadata_callbacks = None

    #Playlistcontainer instance
    _playlistcontainer = None

    def __init__(self, manager, cache_location="", settings_location="", app_key=None, user_agent=None):
        self._manager = manager

        #Callback managers
        self._user_callbacks = spotify.CallbackQueueManager()
        self._metadata_callbacks = spotify.CallbackQueueManager()

        #prepare callbacks
        self._callbacks = _session.callbacks(
            _session.cb_logged_in(self._logged_in),
            _session.cb_logged_out(self._logged_out),
            _session.cb_metadata_updated(self._metadata_updated),
            _session.cb_connection_error(self._connection_error),
            _session.cb_message_to_user(self._message_to_user),
            _session.cb_notify_main_thread(self._notify_main_thread),
            _session.cb_music_delivery(self._music_delivery),
            _session.cb_play_token_lost(self._play_token_lost),
            _session.cb_log_message(self._log_message),
            _session.cb_end_of_track(self._end_of_track),
            _session.cb_streaming_error(self._streaming_error),
            _session.cb_userinfo_updated(self._userinfo_updated),
            _session.cb_start_playback(self._start_playback),
            _session.cb_stop_playback(self._stop_playback),
            _session.cb_get_audio_buffer_stats(self._get_audio_buffer_stats),
        )

        #app key conversion
        appkey_type = ctypes.c_byte * len(app_key)
        appkey_c = appkey_type.from_buffer(bytearray(app_key))

        #initialize app config
        config = _session.config(
            self.api_version,
            cache_location,
            settings_location,
            appkey_c,
            ctypes.sizeof(appkey_c),
            user_agent,
            ctypes.pointer(self._callbacks),
            ctypes.c_void_p(),
            1,
            0,
            1,
        )

        self._session = ctypes.c_void_p()
        err = _session.create(ctypes.byref(config), ctypes.byref(self._session))
        spotify.handle_sp_error(err)

    @synchronized
    def __del__(self):
        _session.release(self._session)

    @synchronized
    def login(self, username, password):
        _session.login(self._session, username, password)

    @synchronized
    def user(self, onload=None):
        user_obj = user.User(self._session, _session.user(self._session))

        if onload != None:
            self._user_callbacks.add_callback(
                user_obj.is_loaded, onload, user_obj
            )

        return user_obj

    @synchronized
    def logout(self):
        _session.logout(self._session)

    @synchronized
    def connectionstate(self):
        return _session.connectionstate(self._session)

    @synchronized
    def userdata(self):
        return _session.userdata(self._session)

    @synchronized
    def set_cache_size(self, size):
            _session.set_cache_size(size)

    @synchronized
    def process_events(self):
        next_timeout = ctypes.c_int(0)
        _session.process_events(self._session, ctypes.byref(next_timeout))
        return next_timeout.value / 1000

    @synchronized
    def player_load(self, track):
        pass

    @synchronized
    def player_seek(self, offset):
        pass

    @synchronized
    def player_play(self, play):
        pass

    @synchronized
    def player_unload(self):
        pass

    @synchronized
    def playlistcontainer(self):
        if self._playlistcontainer is None:
            self._playlistcontainer = playlistcontainer.PlaylistContainer(
                self._session,
                _session.playlistcontainer(self._session),
            )

        return self._playlistcontainer

    #Callback proxies
    @synchronized
    def _logged_in(self, session, error):
        self._manager.logged_in(self, error)

    @synchronized
    def _logged_out(self, session):
        self._manager.logged_out(self)

    @synchronized
    def _metadata_updated(self, session):
        self._manager.metadata_updated(self)

    @synchronized
    def _connection_error(self, session, error):
        self._manager.connection_error(self, error)

    @synchronized
    def _message_to_user(self, session, message):
        self._manager.message_to_user(self, message)

    def _notify_main_thread(self, session):
        #Not synchronized, also nonblocking:
        self._manager.notify_main_thread(self)

    def _music_delivery(self, session, format, frames, num_frames):
        if synchronized.get_lock().acquire(False):
            self._manager.music_delivery(self, format, frames, num_frames)
            synchronized.get_lock().release()
        else:
            return 0

    @synchronized
    def _play_token_lost(self, session):
        self._manager.play_token_lost(self)

    @synchronized
    def _log_message(self, session, data):
        self._manager.log_message(self, data)

    @synchronized
    def _end_of_track(self, session, error):
        self._manager.end_of_track(self, error)

    @synchronized
    def _streaming_error(self, session, error):
        self._manager.streaming_error(self, error)

    @synchronized
    def _userinfo_updated(self, session):
        self._user_callbacks.process_callbacks()
        self._manager.userinfo_updated(self)

    @synchronized
    def _start_playback(self, session):
        self._manager.start_playback(self)

    @synchronized
    def _stop_playback(self, session):
        self._manager.stop_playback(self)

    @synchronized
    def _get_audio_buffer_stats(self, session, stats):
        self._manager.get_audio_buffer_stats(self, stats)
<commit_msg>spotify: session: Moved callback proxies out of the Session class.<commit_after>import ctypes

#Import low level api
from _spotify import session as _session

#Import general classes from the high level module
import spotify

#Also import this one's siblings
from spotify import user, playlistcontainer

#Decorators
from spotify.utils.decorators import synchronized

class ProxySessionCallbacks:
    __session = None
    __callbacks = None
    __struct = None

    def __init__(self, session, callbacks):
        self.__session = session
        self.__callbacks = callbacks
        self.__struct = _session.callbacks(
            _session.cb_logged_in(self._logged_in),
            _session.cb_logged_out(self._logged_out),
            _session.cb_metadata_updated(self._metadata_updated),
            _session.cb_connection_error(self._connection_error),
            _session.cb_message_to_user(self._message_to_user),
            _session.cb_notify_main_thread(self._notify_main_thread),
            _session.cb_music_delivery(self._music_delivery),
            _session.cb_play_token_lost(self._play_token_lost),
            _session.cb_log_message(self._log_message),
            _session.cb_end_of_track(self._end_of_track),
            _session.cb_streaming_error(self._streaming_error),
            _session.cb_userinfo_updated(self._userinfo_updated),
            _session.cb_start_playback(self._start_playback),
            _session.cb_stop_playback(self._stop_playback),
            _session.cb_get_audio_buffer_stats(self._get_audio_buffer_stats),
        )

    @synchronized
    def _logged_in(self, session, error):
        self.__callbacks.logged_in(self, error)

    @synchronized
    def _logged_out(self, session):
        self.__callbacks.logged_out(self)

    @synchronized
    def _metadata_updated(self, session):
        self.__callbacks.metadata_updated(self)

    @synchronized
    def _connection_error(self, session, error):
        self.__callbacks.connection_error(self, error)

    @synchronized
    def _message_to_user(self, session, message):
        self.__callbacks.message_to_user(self, message)

    def _notify_main_thread(self, session):
        #Not synchronized, also nonblocking:
        self.__callbacks.notify_main_thread(self)

    def _music_delivery(self, session, format, frames, num_frames):
        if synchronized.get_lock().acquire(False):
            self.__callbacks.music_delivery(self, format, frames, num_frames)
            synchronized.get_lock().release()
        else:
            return 0

    @synchronized
    def _play_token_lost(self, session):
        self.__callbacks.play_token_lost(self)

    @synchronized
    def _log_message(self, session, data):
        self.__callbacks.log_message(self, data)

    @synchronized
    def _end_of_track(self, session, error):
        self.__callbacks.end_of_track(self, error)

    @synchronized
    def _streaming_error(self, session, error):
        self.__callbacks.streaming_error(self, error)

    @synchronized
    def _userinfo_updated(self, session):
        self._user_callbacks.process_callbacks()
        self.__callbacks.userinfo_updated(self)

    @synchronized
    def _start_playback(self, session):
        self.__callbacks.start_playback(self)

    @synchronized
    def _stop_playback(self, session):
        self.__callbacks.stop_playback(self)

    @synchronized
    def _get_audio_buffer_stats(self, session, stats):
        self.__callbacks.get_audio_buffer_stats(self, stats)

    def get_callback_struct(self):
        return self.__struct

class SessionCallbacks:
    def logged_in(self, session, error):
        pass

    def logged_out(self, session):
        pass

    def metadata_updated(self, session):
        pass

    def connection_error(self, session, error):
        pass

    def message_to_user(self, session, message):
        pass

    def notify_main_thread(self, session):
        pass

    def music_delivery(self, format, frames, num_frames):
        pass

    def play_token_lost(self, session):
        pass

    def log_message(self, session, message):
        pass

    def end_of_track(self, session, error):
        pass

    def userinfo_updated(self, session):
        pass

    def start_playback(self, session):
        pass

    def stop_playback(self, session):
        pass

    def get_audio_buffer_stats(self, session, stats):
        pass

#classes
class Session:
    api_version = 7

    __callbacks = None

    _user_callbacks = None
    _metadata_callbacks = None

    #Playlistcontainer instance
    _playlistcontainer = None

    def __init__(self, callbacks, cache_location="", settings_location="", app_key=None, user_agent=None):
        #Callback managers
        self._user_callbacks = spotify.CallbackQueueManager()
        self._metadata_callbacks = spotify.CallbackQueueManager()

        #prepare callbacks
        self.__callbacks = ProxySessionCallbacks(self, callbacks)

        #app key conversion
        appkey_type = ctypes.c_byte * len(app_key)
        appkey_c = appkey_type.from_buffer(bytearray(app_key))

        #initialize app config
        config = _session.config(
            self.api_version,
            cache_location,
            settings_location,
            appkey_c,
            ctypes.sizeof(appkey_c),
            user_agent,
            ctypes.pointer(self.__callbacks.get_callback_struct()),
            ctypes.c_void_p(),
            1,
            0,
            1,
        )

        self._session = ctypes.c_void_p()
        err = _session.create(ctypes.byref(config), ctypes.byref(self._session))
        spotify.handle_sp_error(err)

    @synchronized
    def __del__(self):
        _session.release(self._session)

    @synchronized
    def login(self, username, password):
        _session.login(self._session, username, password)

    @synchronized
    def user(self, onload=None):
        user_obj = user.User(self._session, _session.user(self._session))

        if onload != None:
            self._user_callbacks.add_callback(
                user_obj.is_loaded, onload, user_obj
            )

        return user_obj

    @synchronized
    def logout(self):
        _session.logout(self._session)

    @synchronized
    def connectionstate(self):
        return _session.connectionstate(self._session)

    @synchronized
    def userdata(self):
        return _session.userdata(self._session)

    @synchronized
    def set_cache_size(self, size):
            _session.set_cache_size(size)

    @synchronized
    def process_events(self):
        next_timeout = ctypes.c_int(0)
        _session.process_events(self._session, ctypes.byref(next_timeout))
        return next_timeout.value / 1000

    @synchronized
    def player_load(self, track):
        pass

    @synchronized
    def player_seek(self, offset):
        pass

    @synchronized
    def player_play(self, play):
        pass

    @synchronized
    def player_unload(self):
        pass

    @synchronized
    def playlistcontainer(self):
        if self._playlistcontainer is None:
            self._playlistcontainer = playlistcontainer.PlaylistContainer(
                self._session,
                _session.playlistcontainer(self._session),
            )

        return self._playlistcontainer
<|endoftext|>


<commit_before>from __future__ import unicode_literals
from future.builtins import str
from future.utils import native

import os
from shutil import rmtree
from uuid import uuid4

from mezzanine.conf import settings
from mezzanine.core.templatetags.mezzanine_tags import thumbnail
from mezzanine.galleries.models import Gallery, GALLERIES_UPLOAD_DIR
from mezzanine.utils.tests import TestCase, copy_test_to_media

class GalleriesTests(TestCase):

    def test_gallery_import(self):
        """
        Test that a gallery creates images when given a zip file to
        import, and that descriptions are created.
        """
        zip_name = "gallery.zip"
        copy_test_to_media("mezzanine.core", zip_name)
        title = native(str(uuid4()))  # i.e. Py3 str / Py2 unicode
        gallery = Gallery.objects.create(title=title, zip_import=zip_name)
        images = list(gallery.images.all())
        self.assertTrue(images)
        self.assertTrue(all([image.description for image in images]))
        # Clean up.
        rmtree(os.path.join(settings.MEDIA_ROOT,
                            GALLERIES_UPLOAD_DIR, title))

    def test_thumbnail_generation(self):
        """
        Test that a thumbnail is created and resized.
        """
        try:
            from PIL import Image
        except ImportError:
            return
        image_name = "image.jpg"
        size = (24, 24)
        copy_test_to_media("mezzanine.core", image_name)
        thumb_name = os.path.join(settings.THUMBNAILS_DIR_NAME,
                                  image_name.replace(".", "-%sx%s." % size))
        thumb_path = os.path.join(settings.MEDIA_ROOT, thumb_name)
        thumb_image = thumbnail(image_name, *size)
        self.assertEqual(os.path.normpath(thumb_image.lstrip("/")), thumb_name)
        self.assertNotEqual(os.path.getsize(thumb_path), 0)
        thumb = Image.open(thumb_path)
        self.assertEqual(thumb.size, size)
        # Clean up.
        del thumb
        os.remove(os.path.join(settings.MEDIA_ROOT, image_name))
        os.remove(os.path.join(thumb_path))
        rmtree(os.path.join(os.path.dirname(thumb_path)))
<commit_msg>Update thumbnail test path.<commit_after>from __future__ import unicode_literals
from future.builtins import str
from future.utils import native

import os
from shutil import rmtree
from uuid import uuid4

from mezzanine.conf import settings
from mezzanine.core.templatetags.mezzanine_tags import thumbnail
from mezzanine.galleries.models import Gallery, GALLERIES_UPLOAD_DIR
from mezzanine.utils.tests import TestCase, copy_test_to_media

class GalleriesTests(TestCase):

    def test_gallery_import(self):
        """
        Test that a gallery creates images when given a zip file to
        import, and that descriptions are created.
        """
        zip_name = "gallery.zip"
        copy_test_to_media("mezzanine.core", zip_name)
        title = native(str(uuid4()))  # i.e. Py3 str / Py2 unicode
        gallery = Gallery.objects.create(title=title, zip_import=zip_name)
        images = list(gallery.images.all())
        self.assertTrue(images)
        self.assertTrue(all([image.description for image in images]))
        # Clean up.
        rmtree(os.path.join(settings.MEDIA_ROOT,
                            GALLERIES_UPLOAD_DIR, title))

    def test_thumbnail_generation(self):
        """
        Test that a thumbnail is created and resized.
        """
        try:
            from PIL import Image
        except ImportError:
            return
        image_name = "image.jpg"
        size = (24, 24)
        copy_test_to_media("mezzanine.core", image_name)
        thumb_name = os.path.join(settings.THUMBNAILS_DIR_NAME,
                                  "thumbs-%s" % image_name,
                                  image_name.replace(".", "-%sx%s." % size))
        thumb_path = os.path.join(settings.MEDIA_ROOT, thumb_name)
        thumb_image = thumbnail(image_name, *size)
        self.assertEqual(os.path.normpath(thumb_image.lstrip("/")), thumb_name)
        self.assertNotEqual(os.path.getsize(thumb_path), 0)
        thumb = Image.open(thumb_path)
        self.assertEqual(thumb.size, size)
        # Clean up.
        del thumb
        os.remove(os.path.join(settings.MEDIA_ROOT, image_name))
        os.remove(os.path.join(thumb_path))
        rmtree(os.path.join(os.path.dirname(thumb_path)))
<|endoftext|>


<commit_before>
from datetime import datetime

import pymongo
from flask import Flask, request
app = Flask(__name__)
app.config.from_object('clio.settings')
try:
    app.config.from_envvar('CLIO_SETTINGS')
except RuntimeError:
    app.logger.debug("couldn't load settings from file in envvar. trying /etc/clio/app.conf")
    try:
        app.config.from_pyfile('/etc/clio/app.conf')
    except RuntimeError:
        app.logger.debug("unable to find any settings files. using defaults in local settings module.")

from utils import getBoolean, ExtRequest
app.request_class = ExtRequest

mongo_conn = None
def get_mongo_conn():
    global mongo_conn
    if mongo_conn is None:
        mongo_conn = pymongo.Connection(app.config['MONGO_HOSTS'], app.config['MONGO_PORT']).clio
    return mongo_conn

@app.route("/store/<host>/<sourcetype>/<float:timestamp>", methods=['PUT'])
def store(host, sourcetype, timestamp):

    if request.headers.get('interval', None):
        interval = float(request.headers['interval'])
        offset = timestamp % interval
        timestamp = timestamp - offset

    timestamp = datetime.utcfromtimestamp(timestamp)

    if request.json is not None:
        data = request.json
    else:
        app.logger.debug("data was in raw format")
        data = request.data

    app.logger.info("host: %s, sourcetype: %s, timestamp: %s" % (host, sourcetype, timestamp))

    if request.headers.get('_id', None):
        spec = {'_id': request.headers['_id']}
    else:
        spec = {'ts': timestamp,
                'host': host}

    if getBoolean(request.headers.get('custom', None)):
        doc = data
    else:
        doc = {'ts': timestamp,
               'host': host,
               'data': data}

    db = get_mongo_conn()
    coll = db['{0}_{1}{2:0>2}'.format(sourcetype, timestamp.year, timestamp.month)]
    coll.update(spec, doc, upsert=True, safe=True)

    return "ok"

if __name__ == '__main__':
    app.run(host=app.config['HOST'], port=app.config['PORT'])

<commit_msg>instead of sending every 'extra' metadata parameter as a separate header value, clump them all together and serialize as extended json.<commit_after>
from datetime import datetime
import json

from bson import json_util
import pymongo
from flask import Flask, request
app = Flask(__name__)
app.config.from_object('clio.settings')
try:
    app.config.from_envvar('CLIO_SETTINGS')
except RuntimeError:
    app.logger.debug("couldn't load settings from file in envvar. trying /etc/clio/app.conf")
    try:
        app.config.from_pyfile('/etc/clio/app.conf')
    except RuntimeError:
        app.logger.debug("unable to find any settings files. using defaults in local settings module.")

from utils import ExtRequest
app.request_class = ExtRequest

mongo_conn = None
def get_mongo_conn():
    global mongo_conn
    if mongo_conn is None:
        mongo_conn = pymongo.Connection(app.config['MONGO_HOSTS'], app.config['MONGO_PORT']).clio
    return mongo_conn

@app.route("/store/<host>/<sourcetype>/<float:timestamp>", methods=['PUT'])
def store(host, sourcetype, timestamp):

    timestamp = datetime.utcfromtimestamp(timestamp)

    if request.json is not None:
        data = request.json
    else:
        app.logger.debug("data was in raw format")
        data = request.data

    app.logger.info("host: %s, sourcetype: %s, timestamp: %s" % (host, sourcetype, timestamp))

    extra = request.headers.get('extra', None)
    if extra is not None:
        extra = json.loads(extra, object_hook=json_util.object_hook)

    if extra.get('timestamp_as_id', False):
        spec = {'_id': timestamp}
        index = [('_id', pymongo.DESCENDING)]
    else:
        spec = {'ts': timestamp,
                'host': host}
        index = [('host', pymongo.ASCENDING),
                 ('ts', pymongo.DESCENDING)]

    if extra.get('custom_schema', False):
        doc = data
    else:
        doc = {'ts': timestamp,
               'host': host,
               'data': data}

    db = get_mongo_conn()
    coll = db['{0}_{1}{2:0>2}'.format(sourcetype, timestamp.year, timestamp.month)]
    coll.update(spec, doc, upsert=True, safe=True)
    coll.ensure_index(index, background=True)

    return "ok"

if __name__ == '__main__':
    app.run(host=app.config['HOST'], port=app.config['PORT'])

<|endoftext|>


<commit_before>"""
Message consumer.

"""
from os.path import exists
from urllib.parse import urlparse

from boto3 import Session
from microcosm.api import defaults, typed
from microcosm_logging.decorators import logger

from microcosm_pubsub.backoff import BackoffPolicy
from microcosm_pubsub.reader import SQSFileReader, SQSStdInReader, SQSJsonReader

STDIN = "STDIN"

def is_file(url):
    if exists(url):
        return True

    return exists(urlparse(url).path)

@logger
class SQSConsumer:
    """
    Consume message from a (single) SQS queue.

    """
    def __init__(
        self,
        sqs_client,
        sqs_envelope,
        sqs_queue_url,
        limit,
        wait_seconds,
        backoff_policy,
    ):
        self.sqs_client = sqs_client
        self.sqs_envelope = sqs_envelope
        self.sqs_queue_url = sqs_queue_url
        self.limit = limit
        self.wait_seconds = wait_seconds
        self.backoff_policy = backoff_policy

    def consume(self):
        """
        Consume a batch of messages.

        :returns: a list of `SQSMessage`
        """
        return [
            self.sqs_envelope.parse_raw_message(self, raw_message)
            for raw_message in self.sqs_client.receive_message(
                AttributeNames=[
                    "ApproximateReceiveCount",
                ],
                MaxNumberOfMessages=self.limit,
                QueueUrl=self.sqs_queue_url,
                WaitTimeSeconds=self.wait_seconds,
            ).get("Messages", [])
        ]

    def ack(self, message):
        """
        Acknowledge that a message was processed successfully.

        Deletes the message from the queue.

        """
        self.sqs_client.delete_message(
            QueueUrl=self.sqs_queue_url,
            ReceiptHandle=message.receipt_handle,
        )

    def nack(self, message, visibility_timeout_seconds=None):
        """
        Acknowledge that a message was NOT processed successfully.

        (Re)sets the retry visibility timeout on the message.

        There are three cases here:
         1.  We raised `Nack`; in this case, we explicitly wish to reprocess the message in the
             near future; setting the visibility timeout is the right thing to do.

         2a. We raised some non-`Nack` error and the `BackoffPolicy` HAS a configured value for its
             `message_retry_visibility_timeout_seconds` meaning an operator has chosen to reprocess
             all messages within that time limit; setting the visibility timeout is the right thing to do.

         2b. We raised some non-`Nack` error and the `BackoffPolicy` DOES NOT HAVE a configured value for its
             `message_retry_visibility_timeout_seconds` config meaning that the system will fallback to
             its default behavior. We can either fallback to the SQS queue default (usually 30s) -- meaning
             not setting the visibility timeout -- or we can enforce a default in this library -- using
             the sqs consumer's config and override the SQS queue's visibility with a known, smallish value.

             We choose the latter under the assumption that 30s is too long to reprocess most messages
             as a defult and that long-running handlers will be configured accordingly (see: 2a).

        Therefore: we always invoke `change_message_visibility`

        """
        timeout = self.backoff_policy.compute_backoff_timeout(message, visibility_timeout_seconds)
        self.sqs_client.change_message_visibility(
            QueueUrl=self.sqs_queue_url,
            ReceiptHandle=message.receipt_handle,
            VisibilityTimeout=timeout,
        )

def configure_sqs_client(graph):
    endpoint_url = graph.config.sqs_consumer.endpoint_url
    profile_name = graph.config.sqs_consumer.profile_name
    region_name = graph.config.sqs_consumer.region_name
    session = Session(profile_name=profile_name)
    return session.client(
        "sqs",
        endpoint_url=endpoint_url,
        region_name=region_name,
    )

@defaults(
    endpoint_url=None,
    profile_name=None,
    region_name=None,
    # backoff policy
    backoff_policy="NaiveBackoffPolicy",
    # SQS will not return more than ten messages at a time
    limit=typed(int, default_value=10),
    # SQS will only return a few messages at time unless long polling is enabled (>0)
    wait_seconds=typed(int, default_value=1),
    # On error, change the visibility timeout when nacking
    message_retry_visibility_timeout_seconds=typed(int, default_value=5),
)
def configure_sqs_consumer(graph):
    """
    Configure an SQS consumer.

    """
    sqs_queue_url = graph.config.sqs_consumer.sqs_queue_url
    sqs_event = graph.config.sqs_consumer.sqs_event

    if graph.metadata.testing or sqs_queue_url == "test":
        from unittest.mock import MagicMock
        sqs_client = MagicMock()
    elif sqs_event:  # AWS Lambda invocation
        sqs_client = SQSJsonReader(sqs_event)
    elif sqs_queue_url == STDIN:
        sqs_client = SQSStdInReader()
    elif is_file(sqs_queue_url):
        sqs_client = SQSFileReader(sqs_queue_url)
    else:
        sqs_client = configure_sqs_client(graph)

    backoff_policy_class = BackoffPolicy.choose_backoff_policy(
        graph.config.sqs_consumer.backoff_policy,
    )

    backoff_policy = backoff_policy_class(
        message_retry_visibility_timeout_seconds=graph.config.sqs_consumer.message_retry_visibility_timeout_seconds,
    )

    return SQSConsumer(
        backoff_policy=backoff_policy,
        limit=graph.config.sqs_consumer.limit,
        sqs_client=sqs_client,
        sqs_envelope=graph.sqs_envelope,
        sqs_queue_url=sqs_queue_url,
        wait_seconds=graph.config.sqs_consumer.wait_seconds,
    )
<commit_msg>Sort imports<commit_after>"""
Message consumer.

"""
from os.path import exists
from urllib.parse import urlparse

from boto3 import Session
from microcosm.api import defaults, typed
from microcosm_logging.decorators import logger

from microcosm_pubsub.backoff import BackoffPolicy
from microcosm_pubsub.reader import SQSFileReader, SQSJsonReader, SQSStdInReader

STDIN = "STDIN"

def is_file(url):
    if exists(url):
        return True

    return exists(urlparse(url).path)

@logger
class SQSConsumer:
    """
    Consume message from a (single) SQS queue.

    """
    def __init__(
        self,
        sqs_client,
        sqs_envelope,
        sqs_queue_url,
        limit,
        wait_seconds,
        backoff_policy,
    ):
        self.sqs_client = sqs_client
        self.sqs_envelope = sqs_envelope
        self.sqs_queue_url = sqs_queue_url
        self.limit = limit
        self.wait_seconds = wait_seconds
        self.backoff_policy = backoff_policy

    def consume(self):
        """
        Consume a batch of messages.

        :returns: a list of `SQSMessage`
        """
        return [
            self.sqs_envelope.parse_raw_message(self, raw_message)
            for raw_message in self.sqs_client.receive_message(
                AttributeNames=[
                    "ApproximateReceiveCount",
                ],
                MaxNumberOfMessages=self.limit,
                QueueUrl=self.sqs_queue_url,
                WaitTimeSeconds=self.wait_seconds,
            ).get("Messages", [])
        ]

    def ack(self, message):
        """
        Acknowledge that a message was processed successfully.

        Deletes the message from the queue.

        """
        self.sqs_client.delete_message(
            QueueUrl=self.sqs_queue_url,
            ReceiptHandle=message.receipt_handle,
        )

    def nack(self, message, visibility_timeout_seconds=None):
        """
        Acknowledge that a message was NOT processed successfully.

        (Re)sets the retry visibility timeout on the message.

        There are three cases here:
         1.  We raised `Nack`; in this case, we explicitly wish to reprocess the message in the
             near future; setting the visibility timeout is the right thing to do.

         2a. We raised some non-`Nack` error and the `BackoffPolicy` HAS a configured value for its
             `message_retry_visibility_timeout_seconds` meaning an operator has chosen to reprocess
             all messages within that time limit; setting the visibility timeout is the right thing to do.

         2b. We raised some non-`Nack` error and the `BackoffPolicy` DOES NOT HAVE a configured value for its
             `message_retry_visibility_timeout_seconds` config meaning that the system will fallback to
             its default behavior. We can either fallback to the SQS queue default (usually 30s) -- meaning
             not setting the visibility timeout -- or we can enforce a default in this library -- using
             the sqs consumer's config and override the SQS queue's visibility with a known, smallish value.

             We choose the latter under the assumption that 30s is too long to reprocess most messages
             as a defult and that long-running handlers will be configured accordingly (see: 2a).

        Therefore: we always invoke `change_message_visibility`

        """
        timeout = self.backoff_policy.compute_backoff_timeout(message, visibility_timeout_seconds)
        self.sqs_client.change_message_visibility(
            QueueUrl=self.sqs_queue_url,
            ReceiptHandle=message.receipt_handle,
            VisibilityTimeout=timeout,
        )

def configure_sqs_client(graph):
    endpoint_url = graph.config.sqs_consumer.endpoint_url
    profile_name = graph.config.sqs_consumer.profile_name
    region_name = graph.config.sqs_consumer.region_name
    session = Session(profile_name=profile_name)
    return session.client(
        "sqs",
        endpoint_url=endpoint_url,
        region_name=region_name,
    )

@defaults(
    endpoint_url=None,
    profile_name=None,
    region_name=None,
    # backoff policy
    backoff_policy="NaiveBackoffPolicy",
    # SQS will not return more than ten messages at a time
    limit=typed(int, default_value=10),
    # SQS will only return a few messages at time unless long polling is enabled (>0)
    wait_seconds=typed(int, default_value=1),
    # On error, change the visibility timeout when nacking
    message_retry_visibility_timeout_seconds=typed(int, default_value=5),
)
def configure_sqs_consumer(graph):
    """
    Configure an SQS consumer.

    """
    sqs_queue_url = graph.config.sqs_consumer.sqs_queue_url
    sqs_event = graph.config.sqs_consumer.sqs_event

    if graph.metadata.testing or sqs_queue_url == "test":
        from unittest.mock import MagicMock
        sqs_client = MagicMock()
    elif sqs_event:  # AWS Lambda invocation
        sqs_client = SQSJsonReader(sqs_event)
    elif sqs_queue_url == STDIN:
        sqs_client = SQSStdInReader()
    elif is_file(sqs_queue_url):
        sqs_client = SQSFileReader(sqs_queue_url)
    else:
        sqs_client = configure_sqs_client(graph)

    backoff_policy_class = BackoffPolicy.choose_backoff_policy(
        graph.config.sqs_consumer.backoff_policy,
    )

    backoff_policy = backoff_policy_class(
        message_retry_visibility_timeout_seconds=graph.config.sqs_consumer.message_retry_visibility_timeout_seconds,
    )

    return SQSConsumer(
        backoff_policy=backoff_policy,
        limit=graph.config.sqs_consumer.limit,
        sqs_client=sqs_client,
        sqs_envelope=graph.sqs_envelope,
        sqs_queue_url=sqs_queue_url,
        wait_seconds=graph.config.sqs_consumer.wait_seconds,
    )
<|endoftext|>


<commit_before>import re

import django.views.generic.edit
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils.timezone import now

import core
from core.templatetags.core import ref
from features.associations.models import Association
from features.associations.views import AssociationMixin
from features.contributions.models import Contribution
from features.conversations.views import CreateGestaltConversation
from . import models

class ContributionFormMixin(django.views.generic.edit.FormMixin):

    def can_post(self):
        return self.request.user.has_perms((self.permission_required_post,), self.object)

    def form_valid(self, form):
        form.save()
        return super().form_valid(form)

    def get_form(self):
        if self.can_post():
            return super().get_form()
        return None

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['contributions'] = self.object.container.contributions
        kwargs['instance'] = models.Contribution(
                author=self.request.user.gestalt, container=self.object.container)
        return kwargs

    def has_permission(self):
        self.object = self.get_object()
        if self.request.method == 'GET':
            return super().has_permission()
        elif self.request.method == 'POST':
            return self.can_post()
        else:
            return False

    def post(self, request, *args, **kwargs):
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def get_success_url(self):
        return self.object.get_absolute_url()

class Delete(AssociationMixin, core.views.PermissionMixin, django.views.generic.UpdateView):
    permission_required = 'contributions.delete'
    model = models.Contribution
    fields = []
    template_name = 'contributions/delete.html'

    def get_form_kwargs(self):
        self.association = self.get_association()
        kwargs = super().get_form_kwargs()
        kwargs['instance'].deleted = now()
        return kwargs

    def get_success_url(self):
        return self.association.get_absolute_url()

class ReplyToAuthor(CreateGestaltConversation):
    permission_required = 'contributions.reply_to_author'

    def get_initial(self):
        subject = 'Re: {}'.format(self.contribution.container.subject)
        if self.association.container.is_conversation:
            permalink_url = reverse('conversation', args=[self.association.pk])
        else:
            permalink_url = reverse('content-permalink', args=[self.association.pk])
        text = '{}\n — [{}]({}#{})'.format(
                self.contribution.contribution.text, self.contribution.author,
                permalink_url, ref(self.contribution))
        text = re.sub('^', '> ', text, flags=re.MULTILINE)
        return {'subject': subject, 'text': text}

    def get_permission_object(self):
        self.association = get_object_or_404(
                Association, pk=self.kwargs.get('association_pk'))
        self.contribution = get_object_or_404(
                Contribution, pk=self.kwargs.get('contribution_pk'))
        self.entity = self.contribution.author
        return self.contribution
<commit_msg>Removed unused code<commit_after>import re

import django.views.generic.edit
from django.shortcuts import get_object_or_404
from django.urls import reverse
from django.utils.timezone import now

import core
from core.templatetags.core import ref
from features.associations.models import Association
from features.associations.views import AssociationMixin
from features.contributions.models import Contribution
from features.conversations.views import CreateGestaltConversation
from . import models

class Delete(AssociationMixin, core.views.PermissionMixin, django.views.generic.UpdateView):
    permission_required = 'contributions.delete'
    model = models.Contribution
    fields = []
    template_name = 'contributions/delete.html'

    def get_form_kwargs(self):
        self.association = self.get_association()
        kwargs = super().get_form_kwargs()
        kwargs['instance'].deleted = now()
        return kwargs

    def get_success_url(self):
        return self.association.get_absolute_url()

class ReplyToAuthor(CreateGestaltConversation):
    permission_required = 'contributions.reply_to_author'

    def get_initial(self):
        subject = 'Re: {}'.format(self.contribution.container.subject)
        if self.association.container.is_conversation:
            permalink_url = reverse('conversation', args=[self.association.pk])
        else:
            permalink_url = reverse('content-permalink', args=[self.association.pk])
        text = '{}\n — [{}]({}#{})'.format(
                self.contribution.contribution.text, self.contribution.author,
                permalink_url, ref(self.contribution))
        text = re.sub('^', '> ', text, flags=re.MULTILINE)
        return {'subject': subject, 'text': text}

    def get_permission_object(self):
        self.association = get_object_or_404(
                Association, pk=self.kwargs.get('association_pk'))
        self.contribution = get_object_or_404(
                Contribution, pk=self.kwargs.get('contribution_pk'))
        self.entity = self.contribution.author
        return self.contribution
<|endoftext|>


<commit_before>"""
The World superclass.
A simple world simulator for MicroPsi nodenet agents

Individual world classes must not only inherit from this one, but also be imported here.
"""

__author__ = 'joscha'
__date__ = '10.05.12'

import json
import os
import warnings
import sys
import micropsi_core
from micropsi_core.world import worldadapter
from micropsi_core.world import worldobject
from micropsi_core import tools
from micropsi_core.tools import generate_uid
import logging

WORLD_VERSION = 1.0

class World(object):
    """The environment of MicroPsi agents. The world connects to their nodenets via world adapters."""

    @property
    def uid(self):
        return self.data.get("uid")

    @uid.setter
    def uid(self, identifier):
        self.data["uid"] = identifier

    @property
    def name(self):
        return self.data.get("name", self.data.get("uid"))

    @name.setter
    def name(self, identifier):
        self.data["name"] = identifier

    @property
    def owner(self):
        return self.data.get("owner")

    @owner.setter
    def owner(self, identifier):
        self.data["owner"] = identifier

    @property
    def current_step(self):
        return self.data.get("step", 0)

    @current_step.setter
    def current_step(self, current_step):
        self.data['step'] = current_step

    @property
    def is_active(self):
        return self.data.get("is_active", False)

    @is_active.setter
    def is_active(self, is_active):
        self.data['is_active'] = is_active

    supported_worldadapters = []

    def __init__(self, filename, world_type="", name="", owner="", uid=None, version=WORLD_VERSION):
        """Create a new MicroPsi simulation environment.

        Arguments:
            filename: the path and filename of the world data
            name (optional): the name of the environment
            owner (optional): the user that created this environment
            uid (optional): unique handle of the world; if none is given, it will be generated
        """

        self.logger = logging.getLogger('world_logger');

        # persistent data
        self.data = {
            "version": WORLD_VERSION,  # used to check compatibility of the world data
            "objects": {},
            "agents": {},
            "step": 0
        }

        folder = self.__module__.split('.')
        folder.pop()
        folder = '.'.join(folder)
        self.supported_worldadapters = { cls.__name__:cls for cls in tools.itersubclasses(worldadapter.WorldAdapter, folder=folder) if cls.__name__ in self.supported_worldadapters }

        self.supported_worldobjects = { cls.__name__:cls for cls in tools.itersubclasses(worldobject.WorldObject, folder=folder)
                                        if cls.__name__ not in self.supported_worldadapters}
        # freaky hack.
        self.supported_worldobjects.pop('WorldAdapter', None)
        self.supported_worldobjects['Default'] = worldobject.WorldObject

        self.uid = uid or generate_uid()
        self.owner = owner
        self.name = name or os.path.basename(filename)
        self.filename = filename
        self.agents = {}
        self.objects = {}

        #self.the_image = None

        self.load()

    def load(self, string=None):
        """Load the world state from a file

        Arguments:
            string (optional): if given, the world state is taken from the string instead.
        """
        # try to access file
        if string:
            try:
                self.data.update(json.loads(string))
            except ValueError:
                warnings.warn("Could not read world data from string")
                return False
        else:
            try:
                with open(self.filename) as file:
                    self.data.update(json.load(file))
            except ValueError:
                warnings.warn("Could not read world data")
                return False
            except IOError:
                warnings.warn("Could not open world file: " + self.filename)
        self.data['world_type'] = self.__class__.__name__
        if "version" in self.data and self.data["version"] == WORLD_VERSION:
            self.initialize_world()
            return True
        else:
            warnings.warn("Wrong version of the world data")
            return False

    def get_available_worldadapters(self):
        """ return the list of instantiated worldadapters """
        return self.supported_worldadapters

    def initialize_world(self):
        """Called after reading new world data.

        Parses the nodenet data and set up the non-persistent data structures necessary for efficient
        computation of the world
        """
        for uid, worldobject in self.data['objects'].copy().items():
            if worldobject['type'] in self.supported_worldobjects:
                self.objects[uid] = self.supported_worldobjects[worldobject['type']](self, **worldobject)
            else:
                self.logger.warn('Worldobject of type %s not supported anymore. Deleting object of this type.' % worldobject['type'])
                del self.data['objects'][uid]
        for uid, agent in self.data.get('agents', {}).items():
            try:
                self.agents[uid] = self.supported_worldadapters[agent['type']](self, **agent)
            except KeyError:
                warnings.warn('Worldadapter %s not found, can not spawn agent %s' % (agent['type'], agent['name']))

    def step(self):
        """ advance the simluation """
        for uid in self.objects:
            self.objects[uid].update()
        for uid in self.agents:
            with self.agents[uid].datasource_lock:
                self.agents[uid].update()
        for uid in self.agents.copy():
            if not self.agents[uid].is_alive():
                self.unregister_nodenet(uid)
                #TODO: prevent respawn?
        self.current_step += 1

    def get_world_view(self, step):
        """ returns a list of world objects, and the current step of the simulation """
        return {
            'objects': self.get_world_objects(),
            'agents': self.data.get('agents', {}),
            'current_step': self.current_step,
        }

    def add_object(self, type, position, uid=None, orientation=0.0, name="", parameters=None, **data):
        """
        Add a new object to the current world.

        Arguments:
            type: the type of the object (currently, only "light_source" is supported
            position: a (x, y) tuple with the coordinates
            orientation (optional): an angle, usually between 0 and 2*pi
            name (optional): a readable name for that object
            uid (optional): if omitted, a uid will be generated

        Returns:
            True, uid if successful
            False, errormessage if not
        """
        if not uid:
            uid = tools.generate_uid()
        if type in self.supported_worldobjects:
            self.objects[uid] = self.supported_worldobjects[type](self, type=type, uid=uid, position=position, orientation=orientation, name=name, parameters=parameters, **data)
            return True, uid
        return False, "type not supported"

    def delete_object(self, object_uid):
        if object_uid in self.objects:
            del self.objects[object_uid]
            del self.data['objects'][object_uid]
            return True
        return False

    def get_world_objects(self, type=None):
        """ returns a dictionary of world objects. """
        objects = {}
        if type is None:
            return self.data['objects']
        else:
            for uid, obj in self.data['objects'].items():
                if obj['type'] == type:
                    objects[uid] = obj
        return objects

    def register_nodenet(self, worldadapter, nodenet):
        """Attempts to register a nodenet at this world.

        Returns True, nodenet_uid if successful,
        Returns False, error_message if not successful

        The methods checks if an existing worldadapterish object without a bound nodenet exists, and if not,
        attempts to spawn one. Then the nodenet is bound to it. It is a good idea to make the worldadapter_uid the
        same as the nodenet_uid

        We don't do it the other way around, because the soulless agent body may have been loaded as part of the
        world definition itself.
        """
        if nodenet.uid in self.agents:
            if self.agents[nodenet.uid].__class__.__name__ == worldadapter:
                return True, nodenet.uid
            else:
                return False, "Nodenet agent already exists in this world, but has the wrong type"
        return self.spawn_agent(worldadapter, nodenet.uid, name=nodenet.name)

    def unregister_nodenet(self, nodenet_uid):
        """Removes the connection between a nodenet and its incarnation in this world; may remove the corresponding
        agent object
        """
        if nodenet_uid in self.agents:
            # stop corresponding nodenet
            micropsi_core.runtime.stop_nodenetrunner(nodenet_uid)

            # remove agent
            del self.agents[nodenet_uid]
        if nodenet_uid in self.data['agents']:
            del self.data['agents'][nodenet_uid]

    def spawn_agent(self, worldadapter_name, nodenet_uid, **options):
        """Creates an agent object,

        Returns True, nodenet_uid if successful,
        Returns False, error_message if not successful
        """
        try:
            self.agents[nodenet_uid] = self.supported_worldadapters[worldadapter_name](self, uid=nodenet_uid, **options)
            return True, nodenet_uid
        except AttributeError:
            return False, "Worldadapter \"%s\" not found" % worldadapter_name
        except KeyError:
            return False, "Incompatible Worldadapter for this World."

    def set_object_properties(self, uid, type=None, position=None, orientation=None, name=None, parameters=None):
        """set attributes of the world object 'uid'; only supplied attributes will be changed.

       Arguments:
           uid: the uid of the worldobject. Mandatory.
           type: a new type for the object. Optional
           position: a new position for the object. Optional
           orientation: a new orientation for the object. Optional
           name: a new name for the object. Optional
           parameters: a new dict of parameters for the object. optional.

        Returns True if object exists, otherwise False"""
        if uid in self.objects:
            if type:
                self.objects[uid].type = type
            if position:
                self.objects[uid].position = position
            if orientation:
                self.objects[uid].orientation = orientation
            if name:
                self.objects[uid].name = name
            if parameters:
                self.objects[uid].parameters = parameters
            return True
        return False

    def set_agent_properties(self, uid, position=None, orientation=None, name=None, parameters=None):
        """set attributes of the agent 'uid'; only supplied attributes will be changed.
        Returns True if agent exists, otherwise False"""

        if uid in self.agents:
            if position:
                self.agents[uid].position = position
            if orientation:
                self.agents[uid].orientation = orientation
            if name:
                self.agents[uid].name = name
            if parameters:
                self.agents[uid].parameters = parameters
            return True
        return False

    def get_available_datasources(self, nodenet_uid):
        """Returns the datasource types for a registered nodenet, or None if the nodenet is not registered."""
        if nodenet_uid in self.agents:
            return self.agents[nodenet_uid].get_available_datasources()
        return None

    def get_available_datatargets(self, nodenet_uid):
        """Returns the datatarget types for a registered nodenet, or None if the nodenet is not registered."""
        if nodenet_uid in self.agents:
            return self.agents[nodenet_uid].get_available_datatargets()
        return None

    def get_datasource(self, nodenet_uid, key):
        """allows the nodenet to read a value from a datasource"""
        if nodenet_uid in self.agents:
            return self.agents[nodenet_uid].get_datasource(key)
        return None

    def set_datatarget(self, nodenet_uid, key, value):
        """allows the nodenet to write a value to a datatarget"""
        if nodenet_uid in self.agents:
            return self.agents[nodenet_uid].set_datatarget(key, value)

    def get_datatarget_feedback(self, nodenet_uid, key):
        """allows the nodenet to write a value to a datatarget"""
        if nodenet_uid in self.agents:
            return self.agents[nodenet_uid].get_datatarget_feedback(key)

# imports of individual world types:
try:
    from micropsi_core.world.island import island
except ImportError as e:
    sys.stdout.write("Could not import island world.\nError: %s \n\n" % e.msg)

try:
    from micropsi_core.world.island.structured_objects import structured_objects
except ImportError as e:
    sys.stdout.write("Could not import island world / structured objects.\nError: %s \n\n" % e.msg)

try:
    from micropsi_core.world.minecraft import minecraft
    from micropsi_core.world.minecraft.minecraft import Minecraft
except ImportError as e:
    sys.stdout.write("Could not import minecraft world.\nError: %s \n\n" % e.msg)
<commit_msg>removes superfluous import.<commit_after>"""
The World superclass.
A simple world simulator for MicroPsi nodenet agents

Individual world classes must not only inherit from this one, but also be imported here.
"""

__author__ = 'joscha'
__date__ = '10.05.12'

import json
import os
import warnings
import sys
import micropsi_core
from micropsi_core.world import worldadapter
from micropsi_core.world import worldobject
from micropsi_core import tools
from micropsi_core.tools import generate_uid
import logging

WORLD_VERSION = 1.0

class World(object):
    """The environment of MicroPsi agents. The world connects to their nodenets via world adapters."""

    @property
    def uid(self):
        return self.data.get("uid")

    @uid.setter
    def uid(self, identifier):
        self.data["uid"] = identifier

    @property
    def name(self):
        return self.data.get("name", self.data.get("uid"))

    @name.setter
    def name(self, identifier):
        self.data["name"] = identifier

    @property
    def owner(self):
        return self.data.get("owner")

    @owner.setter
    def owner(self, identifier):
        self.data["owner"] = identifier

    @property
    def current_step(self):
        return self.data.get("step", 0)

    @current_step.setter
    def current_step(self, current_step):
        self.data['step'] = current_step

    @property
    def is_active(self):
        return self.data.get("is_active", False)

    @is_active.setter
    def is_active(self, is_active):
        self.data['is_active'] = is_active

    supported_worldadapters = []

    def __init__(self, filename, world_type="", name="", owner="", uid=None, version=WORLD_VERSION):
        """Create a new MicroPsi simulation environment.

        Arguments:
            filename: the path and filename of the world data
            name (optional): the name of the environment
            owner (optional): the user that created this environment
            uid (optional): unique handle of the world; if none is given, it will be generated
        """

        self.logger = logging.getLogger('world_logger');

        # persistent data
        self.data = {
            "version": WORLD_VERSION,  # used to check compatibility of the world data
            "objects": {},
            "agents": {},
            "step": 0
        }

        folder = self.__module__.split('.')
        folder.pop()
        folder = '.'.join(folder)
        self.supported_worldadapters = { cls.__name__:cls for cls in tools.itersubclasses(worldadapter.WorldAdapter, folder=folder) if cls.__name__ in self.supported_worldadapters }

        self.supported_worldobjects = { cls.__name__:cls for cls in tools.itersubclasses(worldobject.WorldObject, folder=folder)
                                        if cls.__name__ not in self.supported_worldadapters}
        # freaky hack.
        self.supported_worldobjects.pop('WorldAdapter', None)
        self.supported_worldobjects['Default'] = worldobject.WorldObject

        self.uid = uid or generate_uid()
        self.owner = owner
        self.name = name or os.path.basename(filename)
        self.filename = filename
        self.agents = {}
        self.objects = {}

        #self.the_image = None

        self.load()

    def load(self, string=None):
        """Load the world state from a file

        Arguments:
            string (optional): if given, the world state is taken from the string instead.
        """
        # try to access file
        if string:
            try:
                self.data.update(json.loads(string))
            except ValueError:
                warnings.warn("Could not read world data from string")
                return False
        else:
            try:
                with open(self.filename) as file:
                    self.data.update(json.load(file))
            except ValueError:
                warnings.warn("Could not read world data")
                return False
            except IOError:
                warnings.warn("Could not open world file: " + self.filename)
        self.data['world_type'] = self.__class__.__name__
        if "version" in self.data and self.data["version"] == WORLD_VERSION:
            self.initialize_world()
            return True
        else:
            warnings.warn("Wrong version of the world data")
            return False

    def get_available_worldadapters(self):
        """ return the list of instantiated worldadapters """
        return self.supported_worldadapters

    def initialize_world(self):
        """Called after reading new world data.

        Parses the nodenet data and set up the non-persistent data structures necessary for efficient
        computation of the world
        """
        for uid, worldobject in self.data['objects'].copy().items():
            if worldobject['type'] in self.supported_worldobjects:
                self.objects[uid] = self.supported_worldobjects[worldobject['type']](self, **worldobject)
            else:
                self.logger.warn('Worldobject of type %s not supported anymore. Deleting object of this type.' % worldobject['type'])
                del self.data['objects'][uid]
        for uid, agent in self.data.get('agents', {}).items():
            try:
                self.agents[uid] = self.supported_worldadapters[agent['type']](self, **agent)
            except KeyError:
                warnings.warn('Worldadapter %s not found, can not spawn agent %s' % (agent['type'], agent['name']))

    def step(self):
        """ advance the simluation """
        for uid in self.objects:
            self.objects[uid].update()
        for uid in self.agents:
            with self.agents[uid].datasource_lock:
                self.agents[uid].update()
        for uid in self.agents.copy():
            if not self.agents[uid].is_alive():
                self.unregister_nodenet(uid)
                #TODO: prevent respawn?
        self.current_step += 1

    def get_world_view(self, step):
        """ returns a list of world objects, and the current step of the simulation """
        return {
            'objects': self.get_world_objects(),
            'agents': self.data.get('agents', {}),
            'current_step': self.current_step,
        }

    def add_object(self, type, position, uid=None, orientation=0.0, name="", parameters=None, **data):
        """
        Add a new object to the current world.

        Arguments:
            type: the type of the object (currently, only "light_source" is supported
            position: a (x, y) tuple with the coordinates
            orientation (optional): an angle, usually between 0 and 2*pi
            name (optional): a readable name for that object
            uid (optional): if omitted, a uid will be generated

        Returns:
            True, uid if successful
            False, errormessage if not
        """
        if not uid:
            uid = tools.generate_uid()
        if type in self.supported_worldobjects:
            self.objects[uid] = self.supported_worldobjects[type](self, type=type, uid=uid, position=position, orientation=orientation, name=name, parameters=parameters, **data)
            return True, uid
        return False, "type not supported"

    def delete_object(self, object_uid):
        if object_uid in self.objects:
            del self.objects[object_uid]
            del self.data['objects'][object_uid]
            return True
        return False

    def get_world_objects(self, type=None):
        """ returns a dictionary of world objects. """
        objects = {}
        if type is None:
            return self.data['objects']
        else:
            for uid, obj in self.data['objects'].items():
                if obj['type'] == type:
                    objects[uid] = obj
        return objects

    def register_nodenet(self, worldadapter, nodenet):
        """Attempts to register a nodenet at this world.

        Returns True, nodenet_uid if successful,
        Returns False, error_message if not successful

        The methods checks if an existing worldadapterish object without a bound nodenet exists, and if not,
        attempts to spawn one. Then the nodenet is bound to it. It is a good idea to make the worldadapter_uid the
        same as the nodenet_uid

        We don't do it the other way around, because the soulless agent body may have been loaded as part of the
        world definition itself.
        """
        if nodenet.uid in self.agents:
            if self.agents[nodenet.uid].__class__.__name__ == worldadapter:
                return True, nodenet.uid
            else:
                return False, "Nodenet agent already exists in this world, but has the wrong type"
        return self.spawn_agent(worldadapter, nodenet.uid, name=nodenet.name)

    def unregister_nodenet(self, nodenet_uid):
        """Removes the connection between a nodenet and its incarnation in this world; may remove the corresponding
        agent object
        """
        if nodenet_uid in self.agents:
            # stop corresponding nodenet
            micropsi_core.runtime.stop_nodenetrunner(nodenet_uid)

            # remove agent
            del self.agents[nodenet_uid]
        if nodenet_uid in self.data['agents']:
            del self.data['agents'][nodenet_uid]

    def spawn_agent(self, worldadapter_name, nodenet_uid, **options):
        """Creates an agent object,

        Returns True, nodenet_uid if successful,
        Returns False, error_message if not successful
        """
        try:
            self.agents[nodenet_uid] = self.supported_worldadapters[worldadapter_name](self, uid=nodenet_uid, **options)
            return True, nodenet_uid
        except AttributeError:
            return False, "Worldadapter \"%s\" not found" % worldadapter_name
        except KeyError:
            return False, "Incompatible Worldadapter for this World."

    def set_object_properties(self, uid, type=None, position=None, orientation=None, name=None, parameters=None):
        """set attributes of the world object 'uid'; only supplied attributes will be changed.

       Arguments:
           uid: the uid of the worldobject. Mandatory.
           type: a new type for the object. Optional
           position: a new position for the object. Optional
           orientation: a new orientation for the object. Optional
           name: a new name for the object. Optional
           parameters: a new dict of parameters for the object. optional.

        Returns True if object exists, otherwise False"""
        if uid in self.objects:
            if type:
                self.objects[uid].type = type
            if position:
                self.objects[uid].position = position
            if orientation:
                self.objects[uid].orientation = orientation
            if name:
                self.objects[uid].name = name
            if parameters:
                self.objects[uid].parameters = parameters
            return True
        return False

    def set_agent_properties(self, uid, position=None, orientation=None, name=None, parameters=None):
        """set attributes of the agent 'uid'; only supplied attributes will be changed.
        Returns True if agent exists, otherwise False"""

        if uid in self.agents:
            if position:
                self.agents[uid].position = position
            if orientation:
                self.agents[uid].orientation = orientation
            if name:
                self.agents[uid].name = name
            if parameters:
                self.agents[uid].parameters = parameters
            return True
        return False

    def get_available_datasources(self, nodenet_uid):
        """Returns the datasource types for a registered nodenet, or None if the nodenet is not registered."""
        if nodenet_uid in self.agents:
            return self.agents[nodenet_uid].get_available_datasources()
        return None

    def get_available_datatargets(self, nodenet_uid):
        """Returns the datatarget types for a registered nodenet, or None if the nodenet is not registered."""
        if nodenet_uid in self.agents:
            return self.agents[nodenet_uid].get_available_datatargets()
        return None

    def get_datasource(self, nodenet_uid, key):
        """allows the nodenet to read a value from a datasource"""
        if nodenet_uid in self.agents:
            return self.agents[nodenet_uid].get_datasource(key)
        return None

    def set_datatarget(self, nodenet_uid, key, value):
        """allows the nodenet to write a value to a datatarget"""
        if nodenet_uid in self.agents:
            return self.agents[nodenet_uid].set_datatarget(key, value)

    def get_datatarget_feedback(self, nodenet_uid, key):
        """allows the nodenet to write a value to a datatarget"""
        if nodenet_uid in self.agents:
            return self.agents[nodenet_uid].get_datatarget_feedback(key)

# imports of individual world types:
try:
    from micropsi_core.world.island import island
except ImportError as e:
    sys.stdout.write("Could not import island world.\nError: %s \n\n" % e.msg)

try:
    from micropsi_core.world.island.structured_objects import structured_objects
except ImportError as e:
    sys.stdout.write("Could not import island world / structured objects.\nError: %s \n\n" % e.msg)

try:
    from micropsi_core.world.minecraft import minecraft
except ImportError as e:
    sys.stdout.write("Could not import minecraft world.\nError: %s \n\n" % e.msg)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# content/doc/misc/_wsgi-app.py
# =============================================================================
# WSGI application template.
# Usage:
#     $ uwsgi --http :8765 --master                 \
#                          --processes `nproc`      \
#                          --callable   app         \
#                          --wsgi-file _wsgi-app.py
#
#     $ curl localhost:8765; lynx localhost:8765
# =============================================================================
# See for ref.: https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html
#

TXT = "\nWSGI application template.\n"

RSC_HTTP_200_OK    = "200"
HDR_CONTENT_TYPE_N = "Content-Type"
HDR_CONTENT_TYPE_V = "text/plain"

# The application entry point.
def app(env, resp):
    _env = str(env).encode()
    _txt = str(TXT).encode()

    resp(RSC_HTTP_200_OK,
       [(HDR_CONTENT_TYPE_N,
         HDR_CONTENT_TYPE_V)])

    return [_env, _txt]

# vim:set nu et ts=4 sw=4:
<commit_msg>WSGI app template: JSONify HTTP response.<commit_after># -*- coding: utf-8 -*-
# content/doc/misc/_wsgi-app.py
# =============================================================================
# WSGI application template.
# Usage:
#     $ uwsgi --http :8765 --master                 \
#                          --processes `nproc`      \
#                          --callable   app         \
#                          --wsgi-file _wsgi-app.py
#
#     $ curl localhost:8765; lynx localhost:8765
# =============================================================================
# See for ref.: https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html
#

import json

ENV_N = "envvars"
APP_N = "appname"
APP_V = "WSGI application template"

RSC_HTTP_200_OK    = "200"
HDR_CONTENT_TYPE_N = "Content-Type"
HDR_CONTENT_TYPE_V = "application/json"

# The application entry point.
def app(env, resp):
    # JSONifying the response.
    resp_buffer = json.dumps({
        ENV_N : str(env),
        APP_N : APP_V
    })

    # Adding headers to the response.
    resp(RSC_HTTP_200_OK,
       [(HDR_CONTENT_TYPE_N,
         HDR_CONTENT_TYPE_V)])

    return resp_buffer.encode()

# vim:set nu et ts=4 sw=4:
<|endoftext|>


<commit_before>from abc import ABCMeta, abstractproperty, abstractmethod

class Extractor(object):

    ''' Base Extractor class. '''

    def __init__(self, name=None):
        if name is None:
            name = self.__class__.__name__
        self.name = name

    __metaclass__ = ABCMeta

    @abstractmethod
    def apply(self):
        pass

class StimExtractor(Extractor):

    ''' Abstract class for stimulus-specific extractors. Defines a target Stim
    class that all subclasses must override. '''
    @abstractproperty
    def target(self):
        pass

class ExtractorCollection(Extractor):

    def __init__(self, extractors=None):
        if extractors is None:
            extractors = []
        self.extractors = extractors

    def apply(self, stim):
        pass

def get_extractor(name):
    ''' Scans list of currently available Extractor classes and returns an
    instantiation of the first one whose name perfectly matches
    (case-insensitive).
    Args:
        name (str): The name of the extractor to retrieve. Case-insensitive;
            e.g., 'stftextractor' or 'CornerDetectionExtractor'. For
            convenience, the 'extractor' suffix can be dropped--i.e., passing
            'stft' is equivalent to passing 'stftextractor'.
    '''

    if not name.lower().endswith('extractor'):
        name += 'extractor'

    # Recursively get all classes that inherit from Extractor
    def get_subclasses(cls):
        subclasses = []
        for sc in cls.__subclasses__():
            subclasses.append(sc)
            subclasses.extend(get_subclasses(sc))
        return subclasses

    extractors = get_subclasses(StimExtractor) + get_subclasses(ExtractorCollection)

    for a in extractors:
        if a.__name__.lower().split('.')[-1] == name.lower():
            return a()

    raise KeyError("No extractor named '%s' found." % name)
<commit_msg>fix extractor lookup and extend collections<commit_after>from abc import ABCMeta, abstractproperty, abstractmethod
from featurex import Value

class Extractor(object):

    ''' Base Extractor class. '''

    def __init__(self, name=None):
        if name is None:
            name = self.__class__.__name__
        self.name = name

    __metaclass__ = ABCMeta

    @abstractmethod
    def apply(self):
        pass

class StimExtractor(Extractor):

    ''' Abstract class for stimulus-specific extractors. Defines a target Stim
    class that all subclasses must override. '''
    @abstractproperty
    def target(self):
        pass

class ExtractorCollection(StimExtractor):

    def __init__(self, extractors=None):
        if extractors is None:
            extractors = []
        self.extractors = [get_extractor(s) for s in extractors]
        super(StimExtractor, self).__init__()

    def apply(self, stim, *args, **kwargs):
        return stim.extract(self.extractors, *args, **kwargs)

def get_extractor(name):
    ''' Scans list of currently available Extractor classes and returns an
    instantiation of the first one whose name perfectly matches
    (case-insensitive).
    Args:
        name (str): The name of the extractor to retrieve. Case-insensitive;
            e.g., 'stftextractor' or 'CornerDetectionExtractor'. For
            convenience, the 'extractor' suffix can be dropped--i.e., passing
            'stft' is equivalent to passing 'stftextractor'.
    '''

    name = name.lower()
    if not (name.endswith('extractor') or name.endswith('collection')):
        name += 'extractor'

    # Import all submodules so we have a comprehensive list of extractors
    import audio
    import image
    import text
    import video

    # Recursively get all classes that inherit from Extractor
    def get_subclasses(cls):
        subclasses = []
        for sc in cls.__subclasses__():
            subclasses.append(sc)
            subclasses.extend(get_subclasses(sc))
        return subclasses

    extractors = get_subclasses(Extractor)
    for a in extractors:
        if a.__name__.lower().split('.')[-1] == name.lower():
            return a()

    raise KeyError("No extractor named '%s' found." % name)
<|endoftext|>