def unbind(self, event, handler): """Unbind a callable `handler` from an `event`. Parameters ---------- event : 3-tuple The event to unbind the function from. handler : callable The function that will no longer be called if the event occurs. """ if event not in self._handlers: return # Make sure no access to `_handlers` while its being changed with threading.Lock(): # Notification events if event.is_notification and handler in self._handlers[event]: self._handlers[event].remove(handler) if not self._handlers[event]: del self._handlers[event] # Intervention events - unbind and replace with default if event.is_intervention and self._handlers[event] == handler: self._handlers[event] = evt.get_default_handler(event) # Unbind from our child Association events for assoc in self.active_associations: assoc.unbind(event, handler)
def unbind(self, event, handler): """Unbind a callable `handler` from an `event`. .. versionadded:: 1.3 Parameters ---------- event : 3-tuple The event to unbind the function from. handler : callable The function that will no longer be called if the event occurs. """ if event not in self._handlers: return # Notification events if event.is_notification: handlers = [hh[0] for hh in self._handlers[event]] try: ii = handlers.index(handler) del self._handlers[event][ii] except ValueError: pass if not self._handlers[event]: del self._handlers[event] # Intervention events - unbind and replace with default if event.is_intervention and handler in self._handlers[event]: self._handlers[event] = (evt.get_default_handler(event), None) # Unbind from our child Association events for assoc in self.active_associations: assoc.unbind(event, handler)
def _check_sop_class_extended(self): """Check the user's response to a SOP Class Extended request. Returns ------- list of pdu_primitives.SOPClassExtendedNegotiation The SOP Class Extended Negotiation items to be sent in response """ # pylint: disable=broad-except try: default = evt.get_default_handler(evt.EVT_SOP_EXTENDED) if self.assoc.get_handlers(evt.EVT_SOP_EXTENDED) != default: user_response = evt.trigger( self.assoc, evt.EVT_SOP_EXTENDED, {'app_info' : self.assoc.requestor.sop_class_extended} ) else: user_response = self.assoc.ae.on_sop_class_extended( self.assoc.requestor.sop_class_extended ) except Exception as exc: user_response = {} LOGGER.error( "Exception raised in user's 'on_sop_class_extended' " "implementation" ) LOGGER.exception(exc) if not isinstance(user_response, (type(None), dict)): LOGGER.error( "Invalid type returned by user's 'on_sop_class_extended' " "implementation" ) user_response = {} if not user_response: return [] items = [] for sop_class, app_info in user_response.items(): try: item = SOPClassExtendedNegotiation() item.sop_class_uid = sop_class item.service_class_application_information = app_info items.append(item) except Exception as exc: LOGGER.error( "Unable to set the SOP Class Extended Negotiation " "response values for the SOP Class UID {}" .format(sop_class) ) LOGGER.exception(exc) return items
def _bind_defaults(self) -> None: """Bind the default event handlers.""" # Intervention event handlers for event in evt._INTERVENTION_EVENTS: handler = evt.get_default_handler(event) self.bind(event, handler) # Notification event handlers if _config.LOG_HANDLER_LEVEL == "standard": self.bind(evt.EVT_DIMSE_RECV, standard_dimse_recv_handler) self.bind(evt.EVT_DIMSE_SENT, standard_dimse_sent_handler) self.bind(evt.EVT_PDU_RECV, standard_pdu_recv_handler) self.bind(evt.EVT_PDU_SENT, standard_pdu_sent_handler)
def _check_sop_class_common_extended(self): """Check the user's response to a SOP Class Common Extended request. Returns ------- dict The {SOP Class UID : SOPClassCommonExtendedNegotiation} items for the accepted SOP Class Common Extended negotiation items. """ # pylint: disable=broad-except try: default = evt.get_default_handler(evt.EVT_SOP_COMMON) if self.assoc.get_handlers(evt.EVT_SOP_COMMON) != default: rsp = evt.trigger( self.assoc, evt.EVT_SOP_COMMON, {'items' : self.assoc.requestor.sop_class_common_extended} ) else: rsp = self.assoc.ae.on_sop_class_common_extended( self.assoc.requestor.sop_class_common_extended ) except Exception as exc: LOGGER.error( "Exception raised in user's 'on_sop_class_common_extended' " "implementation" ) LOGGER.exception(exc) return {} try: rsp = { uid:ii for uid, ii in rsp.items() if isinstance(ii, SOPClassCommonExtendedNegotiation) } except Exception as exc: LOGGER.error( "Invalid type returned by user's 'evt.EVT_SOP_COMMON' handler" ) LOGGER.exception(exc) return {} return rsp
def _check_async_ops(self): """Check the user's response to an Asynchronous Operations request. Returns ------- pdu_primitives.AsynchronousOperationsWindowNegotiation or None If the `AE.on_async_ops_window` callback hasn't been implemented then returns None, otherwise returns an AsynchronousOperationsWindowNegotiation item with the default values for the number of operations invoked/performed (1, 1). """ # pylint: disable=broad-except try: # TODO: refactor in v1.4 # Response is always ignored as async ops is not supported default = evt.get_default_handler(evt.EVT_ASYNC_OPS) if self.assoc.get_handlers(evt.EVT_ASYNC_OPS) != default: inv, perf = self.assoc.requestor.asynchronous_operations _ = evt.trigger( self.assoc, evt.EVT_ASYNC_OPS, {'nr_invoked' : inv, 'nr_performed' : perf} ) else: _ = self.assoc.ae.on_async_ops_window( *self.assoc.requestor.asynchronous_operations ) except NotImplementedError: return None except Exception as exc: LOGGER.error( "Exception raised in user's 'on_async_ops_window' " "implementation" ) LOGGER.exception(exc) item = AsynchronousOperationsWindowNegotiation() item.maximum_number_operations_invoked = 1 item.maximum_number_operations_performed = 1 return item
def SCP(self, req, context, info): """The implementation for the DIMSE N-GET service. Parameters ---------- req : dimse_primitives.C_ECHO The N-GET request primitive sent by the peer. context : presentation.PresentationContext The presentation context that the service is operating under. info : dict A dict containing details about the association. See Also -------- ae.ApplicationEntity.on_n_get association.Association.send_n_get Notes ----- **N-GET Request** *Parameters* | (M) Message ID | (M) Requested SOP Class UID | (M) Requested SOP Instance UID | (U) Attribute Identifier List *Attribute Identifier List* An element with VR AT, VM 1-n, containing an attribute tag for each of the attributes applicable to the N-GET operation. **N-GET Response** *Parameters* | (M) Message ID Being Responded To | (U) Affected SOP Class UID | (U) Affected SOP Instance UID | (C) Attribute List | (M) Status *Attribute List* A dataset containing the values of the requested attributes. References ---------- * DICOM Standard, Part 4, `Annex EE <http://dicom.nema.org/medical/dicom/current/output/html/part04.html#chapter_EE>`_ * DICOM Standard, Part 7, Sections `10.1.2 <http://dicom.nema.org/medical/dicom/current/output/html/part07.html#sect_10.1.2>`_, `10.3.2 <http://dicom.nema.org/medical/dicom/current/output/html/part07.html#sect_10.3.2>`_ and `Annex C <http://dicom.nema.org/medical/dicom/current/output/html/part07.html#chapter_C>`_ """ # Build N-GET response primitive rsp = N_GET() rsp.MessageIDBeingRespondedTo = req.MessageID rsp.AffectedSOPClassUID = req.RequestedSOPClassUID rsp.AffectedSOPInstanceUID = req.RequestedSOPInstanceUID default_handler = evt.get_default_handler(evt.EVT_N_GET) if self.assoc.get_handlers(evt.EVT_N_GET) != default_handler: try: (rsp_status, ds) = evt.trigger(self.assoc, evt.EVT_N_GET, { 'request': req, 'context': context.as_tuple, }) except Exception as exc: LOGGER.error( "Exception in the handler bound to 'evt.EVT_N_GET'") LOGGER.exception(exc) # Processing failure - Error in on_n_get callback rsp_status = 0x0110 else: info['parameters'] = { 'message_id': req.MessageID, 'requested_sop_class_uid': req.RequestedSOPClassUID, 'requested_sop_instance_uid': req.RequestedSOPInstanceUID, } # Attempt to run the ApplicationEntity's on_n_get callback # pylint: disable=broad-except try: # Send the value rather than the element (rsp_status, ds) = self.ae.on_n_get(req.AttributeIdentifierList, context.as_tuple, info) except Exception as exc: LOGGER.error( "Exception in the ApplicationEntity.on_n_get() callback") LOGGER.exception(exc) # Processing failure - Error in on_n_get callback rsp_status = 0x0110 # Validate rsp_status and set rsp.Status accordingly rsp = self.validate_status(rsp_status, rsp) # Success or Warning, must return AttributeList dataset if code_to_category(rsp.Status) in [STATUS_SUCCESS, STATUS_WARNING]: # Encode the `dataset` using the agreed transfer syntax # Will return None if failed to encode transfer_syntax = context.transfer_syntax[0] bytestream = encode(ds, transfer_syntax.is_implicit_VR, transfer_syntax.is_little_endian) if bytestream is not None: rsp.AttributeList = BytesIO(bytestream) else: LOGGER.error("Failed to encode the supplied Dataset") # Processing failure - Failed to encode dataset rsp.Status = 0x0110 self.dimse.send_msg(rsp, context.context_id)
def _check_user_identity(self): """Check the user's response to a User Identity request. Returns ------- bool True if the user identity has been confirmed, False otherwise. pdu_primitives.UserIdentityNegotiation or None The negotiation response, if a positive response is requested, otherwise None. """ # pylint: disable=broad-except # The UserIdentityNegotiation (request) item req = self.assoc.requestor.user_identity try: default = evt.get_default_handler(evt.EVT_USER_ID) if self.assoc.get_handlers(evt.EVT_USER_ID) != default: identity_verified, response = evt.trigger( self.assoc, evt.EVT_USER_ID, { 'user_id_type' : req.user_identity_type, 'primary_field' : req.primary_field, 'secondary_field' : req.secondary_field, } ) else: identity_verified, response = self.assoc.ae.on_user_identity( req.user_identity_type, req.primary_field, req.secondary_field, { 'requestor' : self.assoc.requestor.info, } ) except NotImplementedError: # If the user hasn't implemented identity negotiation then # default to accepting the association return True, None except Exception as exc: # If the user has implemented identity negotiation but an exception # occurred then reject the association LOGGER.error("Exception in handling user identity negotiation") LOGGER.exception(exc) return False, None if not identity_verified: # Reject association as the user isn't authorised return False, None if req.user_identity_type in [3, 4, 5]: if req.positive_response_requested and response is not None: try: rsp = UserIdentityNegotiation() rsp.server_response = response return True, rsp except Exception as exc: # > If the acceptor doesn't support user identification it # > will accept the association without making a positive # > response LOGGER.error( "Unable to set the User Identity Negotiation's " "'server_response'" ) LOGGER.exception(exc) return True, None return True, None