Example #1
0
def test_native_id_substitution():
    """Test that the native id gets serialized, when specified."""
    native_id = 'id1'
    # Create measurement and material with two ids
    mat = MaterialRun("A material",
                      uids={
                          native_id: str(uuid4()),
                          "an_id": str(uuid4()),
                          "another_id": str(uuid4())
                      })
    meas = MeasurementRun("A measurement",
                          material=mat,
                          uids={
                              "some_id": str(uuid4()),
                              native_id: str(uuid4()),
                              "an_id": str(uuid4())
                          })

    # Turn the material pointer into a LinkByUID using native_id
    subbed = substitute_links(meas, native_uid=native_id)
    assert subbed.material == LinkByUID.from_entity(mat, name=native_id)

    # Put the measurement into a list and convert that into a LinkByUID using native_id
    measurements_list = [meas]
    subbed = substitute_links(measurements_list, native_uid=native_id)
    assert subbed == [LinkByUID.from_entity(meas, name=native_id)]
Example #2
0
def test_serialized_history():
    """Test the serialization of a complete material history."""
    # Create several runs and specs linked together
    buy_spec = LinkByUID("id", "pr723")
    cookie_dough_spec = MaterialSpec("cookie dough spec", process=buy_spec)
    buy_cookie_dough = ProcessRun("Buy cookie dough", uids={'id': '32283'}, spec=buy_spec)
    cookie_dough = MaterialRun("cookie dough", process=buy_cookie_dough, spec=cookie_dough_spec)
    bake = ProcessRun("bake cookie dough", conditions=[
        Condition("oven temp", origin='measured', value=NominalReal(357, 'degF'))])
    IngredientRun(material=cookie_dough,
                  process=bake, number_fraction=NominalReal(1, ''))
    cookie = MaterialRun("cookie", process=bake, tags=["chocolate chip", "drop"])
    MeasurementRun("taste", material=cookie, properties=[
        Property("taste", value=DiscreteCategorical("scrumptious"))])

    cookie_history = complete_material_history(cookie)
    # There are 7 entities in the serialized list: cookie dough (spec & run), buy cookie dough,
    # cookie dough ingredient, bake cookie dough, cookie, taste
    assert len(cookie_history) == 7
    for entity in cookie_history:
        assert len(entity['uids']) > 0, "Serializing material history should assign uids."

    # Check that the measurement points to the material
    taste_dict = next(x for x in cookie_history if x.get('type') == 'measurement_run')
    cookie_dict = next(x for x in cookie_history if x.get('name') == 'cookie')
    scope = taste_dict.get('material').get('scope')
    assert taste_dict.get('material').get('id') == cookie_dict.get('uids').get(scope)

    # Check that both the material spec and the process run point to the same process spec.
    # Because that spec was initially a LinkByUID, this also tests the methods ability to
    # serialize a LinkByUID.
    cookie_dough_spec_dict = next(x for x in cookie_history if x.get('type') == 'material_spec')
    buy_cookie_dough_dict = next(x for x in cookie_history if x.get('name') == 'Buy cookie dough')
    assert cookie_dough_spec_dict.get('process') == buy_spec.as_dict()
    assert buy_cookie_dough_dict.get('spec') == buy_spec.as_dict()
Example #3
0
def test_object_key_substitution():
    """Test that client can copy a dictionary in which keys are BaseEntity objects."""
    spec = ProcessSpec("A process spec",
                       uids={
                           'id': str(uuid4()),
                           'auto': str(uuid4())
                       })
    run1 = ProcessRun("A process run",
                      spec=spec,
                      uids={
                          'id': str(uuid4()),
                          'auto': str(uuid4())
                      })
    run2 = ProcessRun("Another process run",
                      spec=spec,
                      uids={'id': str(uuid4())})
    process_dict = {spec: [run1, run2]}

    subbed = substitute_links(process_dict, native_uid='auto')
    for key, value in subbed.items():
        assert key == LinkByUID.from_entity(spec, name='auto')
        assert LinkByUID.from_entity(run1, name='auto') in value
        assert LinkByUID.from_entity(run2) in value

    reverse_process_dict = {run2: spec}
    subbed = substitute_links(reverse_process_dict, native_uid='auto')
    for key, value in subbed.items():
        assert key == LinkByUID.from_entity(run2)
        assert value == LinkByUID.from_entity(spec, name='auto')
Example #4
0
 def make_link(entity: BaseEntity):
     if len(entity.uids) == 0:
         raise ValueError("No UID for {}".format(entity))
     elif native_uid and native_uid in entity.uids:
         return LinkByUID(native_uid, entity.uids[native_uid])
     else:
         return LinkByUID.from_entity(entity)
def test_invalid_assignment():
    """Test that invalid assignment throws a TypeError."""
    with pytest.raises(TypeError):
        PropertyAndConditions(property=LinkByUID('id', 'a15'))
    with pytest.raises(TypeError):
        PropertyAndConditions(
            property=Property("property"),
            conditions=[Condition("condition"),
                        LinkByUID('scope', 'id')])
Example #6
0
def test_dictionary_substitution():
    """substitute_objects() should substitute LinkByUIDs that occur in dict keys and values."""
    proc = ProcessRun("A process", uids={'id': '123'})
    mat = MaterialRun("A material", uids={'generic id': '38f8jf'})

    proc_link = LinkByUID.from_entity(proc)
    mat_link = LinkByUID.from_entity(mat)
    index = {(mat_link.scope.lower(), mat_link.id): mat,
             (proc_link.scope.lower(), proc_link.id): proc}

    test_dict = {LinkByUID.from_entity(proc): LinkByUID.from_entity(mat)}
    substitute_objects(test_dict, index)
    assert test_dict[proc] == mat
Example #7
0
def test_object_template_serde():
    """Test serde of an object template."""
    length_template = PropertyTemplate("Length", RealBounds(2.0, 3.5, 'cm'))
    sub_bounds = RealBounds(2.5, 3.0, 'cm')
    color_template = PropertyTemplate("Color", CategoricalBounds(["red", "green", "blue"]))
    # Properties are a mixture of property templates and [template, bounds], pairs
    block_template = MaterialTemplate("Block", properties=[[length_template, sub_bounds],
                                                           color_template])
    copy_template = MaterialTemplate.build(block_template.dump())
    assert copy_template == block_template

    # Tests below exercise similar code, but for measurement and process templates
    pressure_template = ConditionTemplate("pressure", RealBounds(0.1, 0.11, 'MPa'))
    index_template = ParameterTemplate("index", IntegerBounds(2, 10))
    meas_template = MeasurementTemplate("A measurement of length", properties=[length_template],
                                        conditions=[pressure_template], description="Description",
                                        parameters=[index_template], tags=["foo"])
    assert MeasurementTemplate.build(meas_template.dump()) == meas_template

    proc_template = ProcessTemplate("Make an object", parameters=[index_template],
                                    conditions=[pressure_template], allowed_labels=["Label"],
                                    allowed_names=["first sample", "second sample"])
    assert ProcessTemplate.build(proc_template.dump()) == proc_template

    # Check that serde still works if the template is a LinkByUID
    proc_template.conditions[0][0] = LinkByUID('id', pressure_template.uids['id'])
    assert ProcessTemplate.build(proc_template.dump()) == proc_template
Example #8
0
def test_process_id_link():
    """Test that a process run can house a LinkByUID object, and that it survives serde."""
    uid = str(uuid4())
    proc_link = LinkByUID(scope='id', id=uid)
    mat_run = MaterialRun("Another cake", process=proc_link)
    copy_material = loads(dumps(mat_run))
    assert dumps(copy_material) == dumps(mat_run)
Example #9
0
    def _load_and_index(self, d, object_index, substitute=False):
        """
        Load the class based on the type string and index it, if a BaseEntity.

        This function is used as the object hook when deserializing taurus objects

        :param d: dictionary to try to load into a registered class instance
        :param object_index: to add the object to if it is a BaseEntity
        :param substitute: whether to substitute LinkByUIDs when they are found in the index
        :return: the deserialized object, or the input dict if it wasn't recognized
        """
        if "type" not in d:
            return d
        typ = d.pop("type")

        if typ in self._clazz_index:
            clz = self._clazz_index[typ]
            obj = clz.from_dict(d)
        elif typ == LinkByUID.typ:
            obj = LinkByUID.from_dict(d)
            if substitute and (obj.scope.lower(), obj.id) in object_index:
                return object_index[(obj.scope.lower(), obj.id)]
            return obj
        else:
            raise TypeError("Unexpected base object type: {}".format(typ))

        if isinstance(obj, BaseEntity):
            for (scope, uid) in obj.uids.items():
                object_index[(scope.lower(), uid)] = obj
        return obj
Example #10
0
def test_tuple_sub():
    """substitute_objects() should correctly substitute tuple values."""
    proc = ProcessRun('foo', uids={'id': '123'})
    proc_link = LinkByUID.from_entity(proc)
    index = {(proc_link.scope, proc_link.id): proc}
    tup = (proc_link,)
    subbed = substitute_objects(tup, index)
    assert subbed[0] == proc
Example #11
0
def test_template_access():
    """A process run's template should be equal to its spec's template."""
    template = ProcessTemplate("process template", uids={'id': str(uuid4())})
    spec = ProcessSpec("A spec", uids={'id': str(uuid4())}, template=template)
    proc = ProcessRun("A run", uids={'id': str(uuid4())}, spec=spec)
    assert proc.template == template

    proc.spec = LinkByUID.from_entity(spec)
    assert proc.template is None
Example #12
0
def test_template_access():
    """A material run's template should be equal to its spec's template."""
    template = MaterialTemplate("material template", uids={'id': str(uuid4())})
    spec = MaterialSpec("A spec", uids={'id': str(uuid4())}, template=template)
    mat = MaterialRun("A run", uids=['id', str(uuid4())], spec=spec)
    assert mat.template == template

    mat.spec = LinkByUID.from_entity(spec)
    assert mat.template is None
Example #13
0
def test_link_by_uid():
    """Test that linking works."""
    root = MaterialRun(name='root', process=ProcessRun(name='root proc'))
    leaf = MaterialRun(name='leaf', process=ProcessRun(name='leaf proc'))
    IngredientRun(process=root.process, material=leaf)
    IngredientRun(process=root.process, material=LinkByUID.from_entity(leaf))

    copy = loads(dumps(root))
    assert copy.process.ingredients[0].material == copy.process.ingredients[
        1].material
Example #14
0
def test_template_access():
    """A measurement run's template should be equal to its spec's template."""
    template = MeasurementTemplate("measurement template",
                                   uids={'id': str(uuid4())})
    spec = MeasurementSpec("A spec",
                           uids={'id': str(uuid4())},
                           template=template)
    meas = MeasurementRun("A run", uids={'id': str(uuid4())}, spec=spec)
    assert meas.template == template

    meas.spec = LinkByUID.from_entity(spec)
    assert meas.template is None
Example #15
0
    def _deserialize(self, value: dict):
        if 'type' in value and value['type'] == LinkByUID.typ:
            if 'scope' in value and 'id' in value:
                value.pop('type')
                return LinkByUID(**value)
            else:
                raise ValueError(
                    "LinkByUID dictionary must have both scope and id fields")

        raise Exception(
            "Serializable object that is being pointed to must have a self-contained "
            "build() method that does not call deserialize().")
Example #16
0
def test_thin_dumps():
    """Test that thin_dumps turns pointers into links and doesn't work on non-BaseEntity."""
    mat = MaterialRun("The actual material")
    meas_spec = MeasurementSpec("measurement", uids={'my_scope': '324324'})
    meas = MeasurementRun("The measurement", spec=meas_spec, material=mat)

    thin_copy = MeasurementRun.build(json.loads(thin_dumps(meas)))
    assert isinstance(thin_copy, MeasurementRun)
    assert isinstance(thin_copy.material, LinkByUID)
    assert isinstance(thin_copy.spec, LinkByUID)
    assert thin_copy.spec.id == meas_spec.uids['my_scope']

    with pytest.raises(TypeError):
        thin_dumps(LinkByUID('scope', 'id'))
Example #17
0
def _loado(d, index):
    if "type" not in d:
        return d
    typ = d.pop("type")

    if typ in _clazz_index:
        clz = _clazz_index[typ]
        obj = clz.from_dict(d)
    elif typ == LinkByUID.typ:
        obj = LinkByUID.from_dict(d)
        return obj
    else:
        raise TypeError("Unexpected base object type: {}".format(typ))

    if isinstance(obj, BaseEntity):
        for (scope, id) in obj.uids.items():
            index[(scope.lower(), id)] = obj
    return obj
Example #18
0
def test_thin_dumps():
    """Test that thin_dumps turns pointers into links."""
    mat = MaterialRun("The actual material")
    meas_spec = MeasurementSpec("measurement", uids={'my_scope': '324324'})
    meas = MeasurementRun("The measurement", spec=meas_spec, material=mat)

    thin_copy = MeasurementRun.build(json.loads(TaurusJson().thin_dumps(meas)))
    assert isinstance(thin_copy, MeasurementRun)
    assert isinstance(thin_copy.material, LinkByUID)
    assert isinstance(thin_copy.spec, LinkByUID)
    assert thin_copy.spec.id == meas_spec.uids['my_scope']

    # Check that LinkByUID objects are correctly converted their JSON equivalent
    expected_json = '{"id": "my_id", "scope": "scope", "type": "link_by_uid"}'
    assert TaurusJson().thin_dumps(LinkByUID('scope', 'my_id')) == expected_json

    # Check that objects lacking .uid attributes will raise an exception when dumped
    with pytest.raises(TypeError):
        TaurusJson().thin_dumps({{'key': 'value'}})
Example #19
0
def _recursive_substitute(obj, native_uid=None, seen=None):
    if seen is None:
        seen = set({})
    if obj.__hash__ is not None:
        if obj in seen:
            return
        else:
            seen.add(obj)

    if isinstance(obj, (list, tuple)):
        for i, x in enumerate(obj):
            if isinstance(x, BaseEntity):
                if len(x.uids) == 0:
                    raise ValueError("No UID for {}".format(x))
                elif native_uid and native_uid in x.uids:
                    obj[i] = LinkByUID(native_uid, x.uids[native_uid])
                else:
                    uid_to_use = next(iter(x.uids.items()))
                    obj[i] = LinkByUID(uid_to_use[0], uid_to_use[1])
            else:
                _recursive_substitute(x, native_uid, seen)
    elif isinstance(obj, dict):
        for k, x in obj.items():
            if isinstance(x, BaseEntity):
                if len(x.uids) == 0:
                    raise ValueError("No UID for {}".format(x))
                elif native_uid and native_uid in x.uids:
                    obj[k] = LinkByUID(native_uid, x.uids[native_uid])
                else:
                    uid_to_use = next(iter(x.uids.items()))
                    obj[k] = LinkByUID(uid_to_use[0], uid_to_use[1])
            else:
                _recursive_substitute(x, native_uid, seen)
        for k, x in obj.items():
            if isinstance(k, BaseEntity):
                if len(k.uids) == 0:
                    raise ValueError("No UID for {}".format(k))
                elif native_uid and native_uid in k.uids:
                    obj[LinkByUID(native_uid, k.uids[native_uid])] = x
                    del obj[k]
                else:
                    uid_to_use = next(iter(k.uids.items()))
                    obj[LinkByUID(uid_to_use[0], uid_to_use[1])] = x
                    del obj[k]
            else:
                _recursive_substitute(k, native_uid, seen)
    elif isinstance(obj, DictSerializable):
        for k, x in obj.__dict__.items():
            if isinstance(obj, BaseEntity) and k in obj.skip:
                continue
            if isinstance(x, BaseEntity):
                if len(x.uids) == 0:
                    raise ValueError("No UID for {}".format(x))
                elif native_uid and native_uid in x.uids:
                    obj.__dict__[k] = LinkByUID(native_uid, x.uids[native_uid])
                else:
                    uid_to_use = next(iter(x.uids.items()))
                    obj.__dict__[k] = LinkByUID(uid_to_use[0], uid_to_use[1])
            else:
                _recursive_substitute(x, native_uid, seen)
    return
Example #20
0
def test_material_id_link():
    """Check that a measurement can be linked to a material that is a LinkByUID."""
    mat = LinkByUID('id', str(uuid4()))
    meas = MeasurementRun(material=mat)
    assert meas.material == mat
    assert loads(dumps(meas)) == meas