Ejemplo n.º 1
0
 def result(self, process_id):
     """ Get the result of the given process. If the process has not
         completed, returns None. Once the result has been
         successfully fetched, it cannot be fetched again.
     """
     
     completed = False
     try:
         try:
             process = self._get_process(process_id)
             completed = process['completed']
         except ThreadDiedBeforeCompletionError:
             completed = True
             raise
     finally:
         if completed is True:
             del getProcessRegistry(self.context)[process_id]
     
     if completed:
         response = self.request.response
             
         response_details = process.get('result')
         
         status, headers, cookies, body = response_details
         response.setStatus(status)
         
         # Overwriting headers/cookies here is a bit crude, I have
         # tried to use declared interface methods wherever possible
         # but there are some omissions that have to be worked
         # around.
         
         # Currently, ZPublisher.HTTPResponse doesn't implement
         # IHTTPResponse, even though HTTPRequest implements
         # IHTTPRequest.
         current_headers = getattr(response, 'headers',
                           getattr(response, '_headers',
                           {}))
         result_headers = dict([(k.lower(), v) for k, v in headers.items()])
         for h in set(current_headers.keys() + result_headers.keys()):
             if h not in headers and h in current_headers:
                 # no interface-friendly way to unset headers anyway.
                 del current_headers[h]
             else:
                 response.setHeader(h, result_headers[h])
         
         # no interface-friendly way to enumerate response cookies
         # or unset cookies.
         attr = (hasattr(response, 'cookies') and 'cookies') or \
                (hasattr(response, '_cookies') and '_cookies')
         setattr(response, attr, cookies)
         
         if IHTTPResponse.providedBy(response):
             response.setResult(body)
         else:
             response.setBody(body)
         
         return response
     
     else:
         return None
Ejemplo n.º 2
0
    def result(self, process_id):
        """ Get the result of the given process. If the process has not
            completed, returns None. Once the result has been
            successfully fetched, it cannot be fetched again.
        """

        completed = False
        try:
            try:
                process = self._get_process(process_id)
                completed = process['completed']
            except ThreadDiedBeforeCompletionError:
                completed = True
                raise
        finally:
            if completed is True:
                del getProcessRegistry(self.context)[process_id]

        if completed:
            response = self.request.response

            response_details = process.get('result')

            status, headers, cookies, body = response_details
            response.setStatus(status)

            # Overwriting headers/cookies here is a bit crude, I have
            # tried to use declared interface methods wherever possible
            # but there are some omissions that have to be worked
            # around.

            # Currently, ZPublisher.HTTPResponse doesn't implement
            # IHTTPResponse, even though HTTPRequest implements
            # IHTTPRequest.
            current_headers = getattr(response, 'headers',
                                      getattr(response, '_headers', {}))
            result_headers = dict([(k.lower(), v) for k, v in headers.items()])
            for h in set(current_headers.keys() + result_headers.keys()):
                if h not in headers and h in current_headers:
                    # no interface-friendly way to unset headers anyway.
                    del current_headers[h]
                else:
                    response.setHeader(h, result_headers[h])

            # no interface-friendly way to enumerate response cookies
            # or unset cookies.
            attr = (hasattr(response, 'cookies') and 'cookies') or \
                   (hasattr(response, '_cookies') and '_cookies')
            setattr(response, attr, cookies)

            if IHTTPResponse.providedBy(response):
                response.setResult(body)
            else:
                response.setBody(body)

            return response

        else:
            return None
Ejemplo n.º 3
0
    def _get_process(self, process_id):
        # Internal function, used to raise an error if a process with
        # the given ID doesn't exist.

        process = getProcessRegistry(self.context).get(process_id)
        if not process:
            raise NoSuchProcessError
        return process
Ejemplo n.º 4
0
    def _get_process(self, process_id):
        # Internal function, used to raise an error if a process with
        # the given ID doesn't exist.

        process = getProcessRegistry(self.context).get(process_id)
        if not process:
            raise NoSuchProcessError
        return process
Ejemplo n.º 5
0
    def _run(self):
        # Internal function to kick off the asynchronous process.

        process_id = uid_generator()

        # Copy the request: hasattrs for differentiating between
        # zope.publisher and ZPublisher HTTPRequests.
        if hasattr(self.context.REQUEST, 'environ'):
            request_environ = deepcopy(self.context.REQUEST.environ)
        elif hasattr(self.context.REQUEST, '_environ'):
            request_environ = deepcopy(self.context.REQUEST._environ)
        else:
            request_environ = {}
        if hasattr(self.context.REQUEST, 'stdin'):
            self.context.REQUEST.stdin.seek(0)
            request_body = self.context.REQUEST.stdin.read()
        elif hasattr(self.context.REQUEST, 'bodyStream'):
            self.context.REQUEST.bodyStream.stream.seek(0)
            request_body = self.context.REQUEST.bodyStream.stream.read()
        else:
            request_body = ''

        # Pass as little from the current thread to the new thread as
        # possible. Too easy to get lost in ZODB & transaction hell
        # otherwise. Nothing with 'context' or a connection should go
        # through. Function is outside of this class to avoid scope
        # mix-up.
        setup = {
            'request_environ': request_environ,
            'request_body': request_body,
            'pid': process_id
        }

        name = '<%s>' % (process_id)

        # We start a new thread outside of the normal Zope limits,
        # naughty but necessary for now.
        t = threading.Thread(target=process_wrapper, name=name, kwargs=setup)

        getProcessRegistry(self.context)[process_id] = PersistentDict({
            'progress':
            False,
            'result':
            None,
            'completed':
            False
        })
        # Ensure we have committed in this thread before the spawned
        # thread tries to retrieve the process record.
        transaction.commit()

        t.start()

        return process_id
Ejemplo n.º 6
0
 def _run(self):
     # Internal function to kick off the asynchronous process.
         
     process_id = uid_generator()
     
     # Copy the request: hasattrs for differentiating between
     # zope.publisher and ZPublisher HTTPRequests.
     if hasattr(self.context.REQUEST, 'environ'):
         request_environ = deepcopy(self.context.REQUEST.environ)
     elif hasattr(self.context.REQUEST, '_environ'):
         request_environ = deepcopy(self.context.REQUEST._environ)
     else:
         request_environ = {}
     if hasattr(self.context.REQUEST, 'stdin'):
         self.context.REQUEST.stdin.seek(0)
         request_body = self.context.REQUEST.stdin.read()
     elif hasattr(self.context.REQUEST, 'bodyStream'):
         self.context.REQUEST.bodyStream.stream.seek(0)
         request_body = self.context.REQUEST.bodyStream.stream.read()
     else:
         request_body = ''
     
     # Pass as little from the current thread to the new thread as
     # possible. Too easy to get lost in ZODB & transaction hell
     # otherwise. Nothing with 'context' or a connection should go
     # through. Function is outside of this class to avoid scope
     # mix-up.
     setup = {'request_environ': request_environ,
              'request_body': request_body,
              'pid': process_id}
             
     name = '<%s>' % (process_id)
     
     # We start a new thread outside of the normal Zope limits,
     # naughty but necessary for now.
     t = threading.Thread(target=process_wrapper, name=name, kwargs=setup)
     
     getProcessRegistry(self.context)[process_id] = PersistentDict({'progress': False,
                                                                    'result': None,
                                                                    'completed': False})
     # Ensure we have committed in this thread before the spawned
     # thread tries to retrieve the process record.
     transaction.commit()
     
     t.start()
     
     return process_id
Ejemplo n.º 7
0
    def completed(self, process_id, output_json=False):
        """ Return some measure of completeness. If your _run method
            informs of some percentage completeness via the
            _set_progress method, returns a number between 0 and 100,
            otherwise returns False or True.
        """

        if output_json:
            self.context.REQUEST.RESPONSE.setHeader('Content-Type',
                                                    'application/json')

        try:
            process = self._get_process(process_id)
        except NoSuchProcessError:
            if output_json:
                return json.dumps({'completed': 'ERROR'})
            else:
                raise

        progress = process.get('progress', None)
        completed = process.get('completed', False)
        result = process.get('result')
        if isinstance(result, Exception) and \
           completed is not True:
            exception = result
            if not output_json:
                del getProcessRegistry(self.context)[process_id]
                raise ThreadDiedBeforeCompletionError(exception)
            else:
                return json.dumps({'progress': 'ERROR'})

        if not output_json:
            return progress
        else:
            if hasattr(self.context, 'portal_languages'):
                lang = self.context.portal_languages.getPreferredLanguage()
            else:
                lang = 'en'
            progress_message = _(u'percentage_completion',
                                 u'${percentage}% completed...',
                                 mapping={'percentage': progress or 0})
            return json.dumps({
                'progress':
                progress,
                'progress_message':
                translate(progress_message, target_language=lang)
            })
Ejemplo n.º 8
0
 def completed(self, process_id, output_json=False):
     """ Return some measure of completeness. If your _run method
         informs of some percentage completeness via the
         _set_progress method, returns a number between 0 and 100,
         otherwise returns False or True.
     """
     
     if output_json:
         self.context.REQUEST.RESPONSE.setHeader('Content-Type', 'application/json')
     
     try:
         process = self._get_process(process_id)
     except NoSuchProcessError:
         if output_json:
             return json.dumps({'completed': 'ERROR'})
         else:
             raise
         
     progress = process.get('progress', None)
     completed = process.get('completed', False)
     result = process.get('result')
     if isinstance(result, Exception) and \
        completed is not True:
         exception = result
         if not output_json:
             del getProcessRegistry(self.context)[process_id]
             raise ThreadDiedBeforeCompletionError(exception)
         else:
             return json.dumps({'progress': 'ERROR'})
     
     if not output_json:
         return progress
     else:
         if hasattr(self.context, 'portal_languages'):
             lang = self.context.portal_languages.getPreferredLanguage()
         else:
             lang = 'en' 
         progress_message = _(u'percentage_completion',
                              u'${percentage}% completed...',
                              mapping={'percentage': progress or 0})
         return json.dumps({'progress': progress,
                            'progress_message': translate(progress_message,
                                                          target_language=lang)})
Ejemplo n.º 9
0
def process_wrapper(pid, request_body, request_environ):
    # Sets up everything we need to run a view method in a new Zope-ish
    # context, then runs it and stores the result for later retrieval.
    
    _process = None

    def my_mapply(object, positional=(), keyword={},
                   debug=None, maybe=None,
                   missing_name=None,
                   handle_class=None,
                   context=None, bind=0):
        
        if not isinstance(keyword, Mapping):
            keyword = {}
        keyword['process_id'] = pid
        
        args = (getattr(object, '__run__', object),)
        kwargs = dict(positional=positional,
                      keyword=keyword,
                      debug=debug,
                      maybe=maybe,
                      context=context,
                      bind=bind
                      )
        if missing_name is not None:
            kwargs['missing_name'] = missing_name
        if handle_class is not None:
            kwargs['handle_class'] = handle_class
        return mapply(*args, **kwargs)
        
    response = HTTPResponse(stdout=StringIO(), stderr=StringIO())
    request = HTTPRequest(StringIO(request_body), request_environ, response)
    
    request.set('process_id', pid)
    
    import Zope2
    app = Zope2.bobo_application.__bobo_traverse__(request)
    reg = getProcessRegistry(app)
    _process = reg.get(pid)
    
    
    # Run
    try:
        try:
            response = publish(request, 'Zope2', [None], mapply=my_mapply)
            
            # We can't just pass the response back, as the data streams will not
            # be set up right.
            attr = (hasattr(response, 'cookies') and 'cookies') or \
                   (hasattr(response, '_cookies') and '_cookies')
            cookies = deepcopy(getattr(response, attr))
            
            if IHTTPResponse.providedBy(response):
                _process['result'] = (response.getStatus(),
                                      dict(response.getHeaders()),
                                      cookies,
                                      response.consumeBody())
            else:
                # Currently, ZPublisher.HTTPResponse doesn't implement
                # IHTTPResponse, even though HTTPRequest implements
                # IHTTPRequest.
                _process['result'] = (response.getStatus(),
                                      dict(response.headers),
                                      cookies,
                                      response.body)
                
        except Exception, e:
            # Set result to the exception raised
            _process['result'] = e
            raise
        else:
Ejemplo n.º 10
0
def process_wrapper(pid, request_body, request_environ):
    # Sets up everything we need to run a view method in a new Zope-ish
    # context, then runs it and stores the result for later retrieval.

    _process = None

    def my_mapply(object,
                  positional=(),
                  keyword={},
                  debug=None,
                  maybe=None,
                  missing_name=None,
                  handle_class=None,
                  context=None,
                  bind=0):

        if not isinstance(keyword, Mapping):
            keyword = {}
        keyword['process_id'] = pid

        args = (getattr(object, '__run__', object), )
        kwargs = dict(positional=positional,
                      keyword=keyword,
                      debug=debug,
                      maybe=maybe,
                      context=context,
                      bind=bind)
        if missing_name is not None:
            kwargs['missing_name'] = missing_name
        if handle_class is not None:
            kwargs['handle_class'] = handle_class
        return mapply(*args, **kwargs)

    response = HTTPResponse(stdout=StringIO(), stderr=StringIO())
    request = HTTPRequest(StringIO(request_body), request_environ, response)

    request.set('process_id', pid)

    import Zope2
    app = Zope2.bobo_application.__bobo_traverse__(request)
    reg = getProcessRegistry(app)
    _process = reg.get(pid)

    # Run
    try:
        try:
            response = publish(request, 'Zope2', [None], mapply=my_mapply)

            # We can't just pass the response back, as the data streams will not
            # be set up right.
            attr = (hasattr(response, 'cookies') and 'cookies') or \
                   (hasattr(response, '_cookies') and '_cookies')
            cookies = deepcopy(getattr(response, attr))

            if IHTTPResponse.providedBy(response):
                _process['result'] = (response.getStatus(),
                                      dict(response.getHeaders()), cookies,
                                      response.consumeBody())
            else:
                # Currently, ZPublisher.HTTPResponse doesn't implement
                # IHTTPResponse, even though HTTPRequest implements
                # IHTTPRequest.
                _process['result'] = (response.getStatus(),
                                      dict(response.headers), cookies,
                                      response.body)

        except Exception, e:
            # Set result to the exception raised
            _process['result'] = e
            raise
        else: