def _validate_final_value(self, schemas_by_id): if self.final_value_source == UNDEFINED: return assert isinstance(self.final_value_source, ProtocolPath) if self.final_value_source.start_protocol not in schemas_by_id: raise ValueError( f"The value source {self.final_value_source} does not exist.") protocol_schema = schemas_by_id[self.final_value_source.start_protocol] protocol_object = protocol_schema.to_protocol() protocol_object.get_value(self.final_value_source) attribute_type = protocol_object.get_class_attribute( self.final_value_source).type_hint # TODO: In Python < 3.7 the Union type will collapse pint.Quantity # and pint.Measurement into pint.Quantity such that this check # will fail. For now we allow Measurements or Quantities, but # this should be reverted to just pint.Measurement when dropping # 3.6 support. if is_union_type(attribute_type): assert is_type_subclass_of_type(attribute_type, pint.Quantity) else: assert is_type_subclass_of_type(attribute_type, pint.Measurement)
def _validate_final_value(self, schemas_by_id): if self.final_value_source == UNDEFINED: return assert isinstance(self.final_value_source, ProtocolPath) if self.final_value_source.start_protocol not in schemas_by_id: raise ValueError( f"The value source {self.final_value_source} does not exist.") protocol_schema = schemas_by_id[self.final_value_source.start_protocol] protocol_object = protocol_schema.to_protocol() protocol_object.get_value(self.final_value_source) attribute_type = protocol_object.get_class_attribute( self.final_value_source).type_hint assert is_type_subclass_of_type(attribute_type, Observable)
def _validate_gradients(self, schemas_by_id): if self.gradients_sources == UNDEFINED: return assert all(isinstance(x, ProtocolPath) for x in self.gradients_sources) for gradient_source in self.gradients_sources: if gradient_source.start_protocol not in schemas_by_id: raise ValueError( f"The gradient source {gradient_source} does not exist.") protocol_schema = schemas_by_id[gradient_source.start_protocol] protocol_object = protocol_schema.to_protocol() protocol_object.get_value(gradient_source) attribute_type = protocol_object.get_class_attribute( gradient_source).type_hint assert is_type_subclass_of_type(attribute_type, ParameterGradient)
def test_negative_is_type_subclass_of_type(type_a, type_b): """A negative test of the `is_type_subclass_of_type` method.""" assert not is_type_subclass_of_type(type_a, type_b)
def test_positive_is_type_subclass_of_type(type_a, type_b): """A positive test of the `is_type_subclass_of_type` method.""" assert is_type_subclass_of_type(type_a, type_b)
def _validate_interfaces(self, schemas_by_id): """Validates the flow of the data between protocols, ensuring that inputs and outputs correctly match up. """ for protocol_schema in schemas_by_id.values(): protocol_object = protocol_schema.to_protocol() for input_path in protocol_object.required_inputs: input_value = protocol_object.get_value(input_path) input_attribute = protocol_object.get_class_attribute( input_path) if not isinstance(input_attribute, InputAttribute): continue is_optional = input_attribute.optional if input_value == UNDEFINED and is_optional is False: raise ValueError( f"The {input_path} required input of protocol " f"{protocol_schema.id} was not set.") for input_path in protocol_object.required_inputs: value_references = protocol_object.get_value_references( input_path) for source_path, value_reference in value_references.items(): if value_reference.is_global: # We handle global input validation separately continue value_reference = self._get_unreplicated_path( value_reference) # Make sure the other protocol whose output we are interested # in actually exists. if (value_reference.start_protocol not in schemas_by_id and value_reference.start_protocol != protocol_object.id): raise ValueError( f"The {protocol_object.id} protocol tries to take input " f"from a non-existent protocol: {value_reference.full_path}" ) if value_reference.start_protocol != protocol_object.id: other_protocol_schema = schemas_by_id[ value_reference.start_protocol] other_protocol_object = other_protocol_schema.to_protocol( ) else: other_protocol_object = protocol_object unnested_value_reference = self._get_unnested_protocol_path( value_reference) unnested_source_path = self._get_unnested_protocol_path( source_path) # Make sure the other protocol has the output referenced # by this input. other_protocol_object.get_value(unnested_value_reference) # Do a very rudimentary type check between the input and # output types. This is not currently possible for nested # or indexed properties, or outputs of replicated protocols. if (value_reference.full_path != unnested_value_reference.full_path or source_path.full_path != unnested_source_path.full_path): continue is_replicated_reference = False protocol_replicators = self.protocol_replicators if protocol_replicators == UNDEFINED: protocol_replicators = [] for replicator in protocol_replicators: if (replicator.placeholder_id in protocol_schema.id and replicator.placeholder_id in value_reference.protocol_path) or ( replicator.placeholder_id not in protocol_schema.id and replicator.placeholder_id not in value_reference.protocol_path): continue is_replicated_reference = True break if is_replicated_reference: continue expected_input_type = protocol_object.get_class_attribute( unnested_source_path).type_hint expected_output_type = other_protocol_object.get_class_attribute( unnested_value_reference).type_hint if expected_input_type is None or expected_output_type is None: continue if not is_type_subclass_of_type(expected_output_type, expected_input_type): raise ValueError( f"The output type ({expected_output_type}) of " f"{value_reference} does not match the requested " f"input type ({expected_input_type}) of {source_path}." )