def target(sequence, values_cache, errors_cache, q_in, q_out, timeout, start_hook): if start_hook is not None: start_hook() try: while True: try: item, slot = q_in.get(timeout=timeout) except queue.Empty: return if slot < 0: return try: values_cache[slot] = sequence[item] except Exception as error: _, _, trace = sys.exc_info() trace = tblib.Traceback(trace) tb_str = traceback.format_exc(20) # noinspection PyBroadException try: # this one may fail if ev is not picklable errors_cache[slot] = error, trace except Exception: errors_cache[slot] = None, tb_str q_out.put_nowait((item, slot, JobStatus.FAILED)) else: q_out.put_nowait((item, slot, JobStatus.DONE)) except IOError: return # parent probably died
def _unpack_picked_error(self, pickled_error: bytes) -> Status: """Unpacks `pickled_error` into a object of type Status""" s = Status() exc = pickle.loads(pickled_error) s.error_text = traceback.format_exception(*exc)[-1] s.tb = json.dumps(tblib.Traceback(exc[2]).to_dict()) return s
def create_return_value(payload): """Convenience function that creates a return value that can be passed back by a function. The 'payload' should either be a dictionary of return data, or it should be an exception. """ try: if payload is None: return {"status": 0} elif isinstance(payload, Exception): err = { "class": str(payload.__class__.__name__), "module": str(payload.__class__.__module__), "error": str(payload) } if payload.__traceback__ is not None: import tblib as _tblib tb = _tblib.Traceback(payload.__traceback__) err["traceback"] = tb.to_dict() return {"status": -1, "exception": err} elif isinstance(payload, dict): return {"status": 0, "return": payload} else: return {"status": 0, "return": {"result": payload}} except Exception as e: return {"status": -3, "error": str(e)} except: return {"status": -4, "error": "unknown"}
def handle(self, record): record.msg += ' (message from worker {})'.format(self._worker_id) if record.exc_info: record.exc_info = (record.exc_info[0], record.exc_info[1], tblib.Traceback(record.exc_info[2])) try: self._client.call('log', pickle.dumps(record)) except: pass
def wrapper(*args, **kwargs): try: result = dict(code='success', result=func(*args, **kwargs)) except: logger.exception("Exception in child rpc_result") _type, value, _tb = sys.exc_info() exc_info = (_type, value, tblib.Traceback(_tb)) result = dict(code='error', result=exc_info) logger.debug("returning {!r} for".format(result)) return pickle.dumps(result)
def log(self, log_record): try: record = pickle.loads(log_record) if record.exc_info: record.exc_info = (record.exc_info[0], record.exc_info[1], tblib.Traceback( record.exc_info[2].as_traceback())) logging.getLogger(record.name).handle(record) except: pass
def exception_to_safe_exception(e): """Convert the passed exception to a "safe" exception - this is one that can be copied because it does not hold references to any local data """ if not issubclass(e.__class__, Exception): return TypeError(str(e)) import tblib as _tblib tb = _tblib.Traceback(e.__traceback__) e.__traceback__ = tb.as_traceback() return e
def target(pid, func, value_slots, error_slots, job_queue, done_queue, timeout, start_hook): logger = get_logger(__name__) seterr(evaluation='passthrough') if start_hook is not None: start_hook() logger.debug("worker %d: starting", pid) # make 1D bytes views of the buffer slots while True: try: # acquire job slot = job_queue.get(timeout=timeout) except queue.Empty: # or go to sleep try: # notify parent done_queue.put((-pid - 1, 0)) finally: logger.debug("worker %d: timeout, exiting", pid) return except IOError: # parent probably died logger.debug("worker %d: parent died, exiting", pid) return if slot < 0: logger.debug("worker %d: clean termination", pid) return try: # generate and store value value_slots[slot] = func() job_status = JobStatus.DONE except Exception as error: # save error informations if any try: trace_dump = tblib.Traceback(sys.exc_info()[2]) error_slots[slot] = error, trace_dump except Exception: error_slots[slot] = None, None job_status = JobStatus.FAILED try: # notify about job termination done_queue.put((slot, job_status)) except IOError: # parent process died unexpectedly logger.debug("worker %d: parent died, exiting", pid) return
async def worker( consumer_name: str, redis_url: str, worker_id: Optional[str] = None, stopped_event: Optional[Event] = None, ): worker_id = worker_id or uuid4() log = logging.getLogger(f"pyuubin.worker.{worker_id}") db = RedisDb() await db.connect(redis_url) stopped_event = stopped_event or Event() log.info(f"Starting worker: {worker_id} for `{consumer_name}`.") async with db.mail_consumer(consumer_name) as consumer: async for email in consumer.mail_queue(stopped_event): templates = Templates(await db.load_templates()) log.info("Received an email and sending it.") try: await send_mail(email, templates) log.info("Email sent.") except CannotSendMessages as e: await db.add_mail(email) log.error("Cannot send messages.") if (log.getEffectiveLevel() == logging.DEBUG ): # pragma: no cover log.exception(e) stopped_event.set() except (FailedToSendMessage, Exception) as e: et, ev, tb = sys.exc_info() await consumer.report_failed_mail( email, traceback=tblib.Traceback(tb).to_dict()) log.error(f"Failed to send message: {e}") log.exception(e) finally: await consumer.ack_mail(email) log.info("Shutting down the worker.") await db.close()
def target(sequence, buffers, errors_cache, q_in, q_out, timeout, start_hook): signal.signal(signal.SIGINT, signal.SIG_IGN) # let parent handle signals if start_hook is not None: start_hook() buffers = tuple( np.reshape(np.frombuffer(b, t), (-1, ) + s) for b, t, s in buffers) try: while True: try: item, slot = q_in.get(timeout=timeout) except queue.Empty: return if slot < 0: return try: for field, buffer in zip(sequence[item], buffers): buffer[slot] = field except Exception as error: _, _, trace = sys.exc_info() trace = tblib.Traceback(trace) tb_str = traceback.format_exc(20) # noinspection PyBroadException try: # this one may fail if ev is not picklable errors_cache[slot] = error, trace except Exception: errors_cache[slot] = None, tb_str q_out.put_nowait((item, slot, JobStatus.FAILED)) else: q_out.put_nowait((item, slot, JobStatus.DONE)) except IOError: return # parent probably died
def _unpack_picked_error(self, pickled_error: bytes) -> Tuple[str, str]: """Unpacks `pickled_error` into and error `message` and `tb` string.""" exc = pickle.loads(pickled_error) message = traceback.format_exception(*exc)[-1] tb = json.dumps(tblib.Traceback(exc[2]).to_dict()) return message, tb
def post(self, rosname): # fail early if no pyros client if self.node_client is None: self.logger.warn('404 : %s', rosname) return make_response('', 404) try: rosname = '/' + rosname #self.logger.debug('POST') length = int(request.environ['CONTENT_LENGTH']) use_ros = ( 'CONTENT_TYPE' in request.environ and ROS_MSG_MIMETYPE == request.environ['CONTENT_TYPE'].split(';')[0].strip()) services = self.node_client.services() topics = self.node_client.topics() params = self.node_client.params() if rosname in services: mode = 'service' service = services[rosname] elif rosname in topics: mode = 'topic' topic = topics[rosname] elif rosname in params: mode = 'param' param = params[rosname] else: self.logger.warn('404 : %s', rosname) return make_response('', 404) # we are now sending via the client node, which will convert the # received dict into the correct message type for the service (or # break, if it's wrong.) # Trying to parse the input try: input_data = request.environ['wsgi.input'].read(length) input_data = simplejson.loads(input_data or "{}") input_data.pop('_format', None) #TODO : We get the message structure via the topic, can we already use it for checking before calling rospy ? except ValueError as exc_value: raise WrongMessageFormat( message="Your request payload was incorrect: {exc_value}". format(exc_value=exc_value), traceback=tblib.Traceback(sys.exc_info()[2]).to_dict()) # input_msg = input_msg_type() # was topic.rostype but we dont have it here ( cant serialize and transfer easily ) # self.logger.debug('input_msg:%r', input_msg) # if use_ros: # input_msg.deserialize(input_data) # else: # input_data = json.loads(input_data) # input_data.pop('_format', None) # msgconv.populate_instance(input_data, input_msg) response = None try: if mode == 'service': self.logger.debug('calling service %s with msg : %s', service.get('name', None), input_data) ret_msg = self.node_client.service_call( rosname, input_data) if use_ros: content_type = ROS_MSG_MIMETYPE output_data = StringIO() ret_msg.serialize(output_data) output_data = output_data.getvalue() elif ret_msg: output_data = ret_msg # the returned message is already converted from ros format by the client output_data['_format'] = 'ros' output_data = simplejson.dumps(output_data, ignore_nan=True) content_type = 'application/json' else: output_data = "{}" content_type = 'application/json' response = make_response(output_data, 200) response.mimetype = content_type elif mode == 'topic': self.logger.debug('publishing \n%s to topic %s', input_data, topic.get('name', None)) self.node_client.topic_inject(rosname, input_data) response = make_response('{}', 200) response.mimetype = 'application/json' elif mode == 'param': self.logger.debug('setting \n%s param %s', input_data, param.get('name', None)) self.node_client.param_set(rosname, input_data) response = make_response('{}', 200) response.mimetype = 'application/json' return response # converting pyros exceptions to proper rostful exceptions # except (InvalidMessageException, NonexistentFieldException, FieldTypeMismatchException) as exc_value: # raise WrongMessageFormat( # message=str(exc_value.message), # traceback=tblib.Traceback(sys.exc_info()[2]).to_dict() # ) except PyrosServiceTimeout as exc_value: raise ServiceTimeout(message=str(exc_value.message), traceback=tblib.Traceback( sys.exc_info()[2]).to_dict()) except PyrosServiceNotFound as exc_value: raise ServiceNotFound(message=str(exc_value.message), traceback=tblib.Traceback( sys.exc_info()[2]).to_dict()) # returning local exceptions except WrongMessageFormat as wmf: self.logger.error( 'Wrong message format! => {status} \n{exc}'.format( status=wmf.status_code, exc=wmf.message)) return make_response( simplejson.dumps(wmf.to_dict(), ignore_nan=True), wmf.status_code) except ServiceTimeout as st: self.logger.error('Service Timeout! => {status} \n{exc}'.format( status=st.status_code, exc=st.message)) return make_response( simplejson.dumps(st.to_dict(), ignore_nan=True), st.status_code) except ServiceNotFound as snf: self.logger.error('Service Not Found! => {status} \n{exc}'.format( status=snf.status_code, exc=snf.message)) return make_response( simplejson.dumps(snf.to_dict(), ignore_nan=True), snf.status_code) # Generic way to return Exceptions we don't know how to handle # But we can do a tiny bit better if it s a PyrosException except Exception as exc_value: exc_type, exc_value, tb = sys.exc_info() tb = tblib.Traceback(tb) exc_dict = { 'exc_type': str(exc_type), # TODO : it would be nice if pyros exception wouldnt need such a check... 'exc_value': str(exc_value.message) if isinstance(exc_value, PyrosException) else str(exc_value), 'traceback': tb.to_dict() } self.logger.error( 'An exception occurred! => 500 \n{exc}'.format(exc=exc_dict)) return make_response(simplejson.dumps(exc_dict, ignore_nan=True), 500)
def _build_and_evaluate_design(cfg): """ Build the "current" design proposal and evaluate its performance. """ # We want to treat the build process as an # integrated part of the product, so as soon # as we know which system configuration is # to be built, we launch a new (sub)process # so that the remainder of the build process # can be taken from that system configuration. # # We define a pickle file that we use to record # the result of the build and make sure that # any old files with the same name have been # removed from the temporary directory. # dirpath_branch_tmp = cfg['paths']['dirpath_branch_tmp'] filepath_result = os.path.join(dirpath_branch_tmp, 'result.pickle') cfg['paths']['filepath_result'] = filepath_result if os.path.isfile(filepath_result): os.remove(filepath_result) # We pickle the configuration and base64 encode # it so we can send it as a command-line parameter # to the build process. # pickled_cfg = base64.b64encode( pickle.dumps(cfg, protocol=da.constants.PICKLE_PROTOCOL)) # Call the build in a subprocess. # # NOTE: We communicate with the subprocess # using pickled data -- so there are # potential security concerns here and # we need to be careful to keep this # data secure. # dirpath_isolated_src = cfg['paths']['dirpath_isolated_src'] da.lwc.run.python3(['-m', 'da.metabuild', pickled_cfg], dirpath_lwc_root=dirpath_isolated_src) # We expect the subprocess to pickle the # results (Exception or success) in our # defined results file. # if not os.path.isfile(filepath_result): raise RuntimeError( 'Could not find result file from subsidiary build process. ' 'It seems to have terminated unexpectedly.') with open(filepath_result, 'rb', buffering=1) as file_result: result = pickle.load(file_result) # Examine output and raise exception if required. if result == da.constants.BUILD_COMPLETED: return result else: (_, exception_value, trace_back) = result # We want to be able to open our text editor # or IDE at the location where an exception # was thrown - but we don't want to open # the file *inside* the isolation dir, as # our changes will simply be overwritten # on the next build. Instead, we want to # open the *real* source file in our local # working copy. # # To do this, we re-write the contents of # the traceback data structure so that # references to the isolated source # directory get replaced by references # to the "outer" local working copy # directory. # dirpath_outer_lwc_root = cfg['paths']['dirpath_lwc_root'] trace_back_dict = tblib.Traceback(trace_back).to_dict() for obj in da.util.walkobj(trace_back_dict, gen_leaf=False, gen_nonleaf=True, gen_path=False, gen_obj=True): if isinstance(obj, collections.Mapping): for key in ('co_filename', '__file__'): if key in obj: obj[key] = obj[key].replace(dirpath_isolated_src, dirpath_outer_lwc_root) raise exception_value.with_traceback( tblib.Traceback.from_dict(trace_back_dict).as_traceback())