Beispiel #1
0
def test_tiprack_list():
    labware_name = 'opentrons_96_tiprack_300ul'
    labware_def = labware.get_labware_definition(labware_name)
    tiprack = labware.Labware(labware_def,
                              Location(Point(0, 0, 0), 'Test Slot'))
    tiprack_2 = labware.Labware(labware_def,
                                Location(Point(0, 0, 0), 'Test Slot'))

    assert labware.select_tiprack_from_list(
        [tiprack], 1) == (tiprack, tiprack['A1'])

    assert labware.select_tiprack_from_list(
        [tiprack], 1, tiprack.wells()[1]) == (tiprack, tiprack['B1'])

    tiprack['C1'].has_tip = False
    assert labware.select_tiprack_from_list(
        [tiprack], 1, tiprack.wells()[2]) == (tiprack, tiprack['D1'])

    tiprack['H12'].has_tip = False
    tiprack_2['A1'].has_tip = False
    assert labware.select_tiprack_from_list(
        [tiprack, tiprack_2], 1, tiprack.wells()[95]) == (
            tiprack_2, tiprack_2['B1'])

    with pytest.raises(labware.OutOfTipsError):
        labware.select_tiprack_from_list(
            [tiprack], 1, tiprack.wells()[95])
def test_select_next_tip():
    labware_name = 'opentrons_96_tiprack_300ul'
    labware_def = labware.get_labware_definition(labware_name)
    tiprack = labware.Labware(labware_def,
                              Location(Point(0, 0, 0), 'Test Slot'))
    well_list = tiprack.wells()

    next_one = tiprack.next_tip()
    assert next_one == well_list[0]
    next_five = tiprack.next_tip(5)
    assert next_five == well_list[0]
    next_eight = tiprack.next_tip(8)
    assert next_eight == well_list[0]
    next_nine = tiprack.next_tip(9)
    assert next_nine is None

    # A1 tip only has been used
    tiprack.use_tips(well_list[0])

    next_one = tiprack.next_tip()
    assert next_one == well_list[1]
    next_five = tiprack.next_tip(5)
    assert next_five == well_list[1]
    next_eight = tiprack.next_tip(8)
    assert next_eight == well_list[8]

    # 2nd column has also been used
    tiprack.use_tips(well_list[8], num_channels=8)

    next_one = tiprack.next_tip()
    assert next_one == well_list[1]
    next_five = tiprack.next_tip(5)
    assert next_five == well_list[1]
    next_eight = tiprack.next_tip(8)
    assert next_eight == well_list[16]

    # Bottom 4 tips of 1rd column are also used
    tiprack.use_tips(well_list[4], num_channels=4)

    next_one = tiprack.next_tip()
    assert next_one == well_list[1]
    next_three = tiprack.next_tip(3)
    assert next_three == well_list[1]
    next_five = tiprack.next_tip(5)
    assert next_five == well_list[16]
    next_eight = tiprack.next_tip(8)
    assert next_eight == well_list[16]

    # you can reuse tips infinitely on api level 2.2
    tiprack.use_tips(well_list[0])
    tiprack.use_tips(well_list[0])

    # you can't on api level 2.1 or previous
    early_tr = labware.Labware(labware_def,
                               Location(Point(0, 0, 0), 'Test Slot'),
                               api_level=APIVersion(2, 1))
    early_tr.use_tips(well_list[0])
    with pytest.raises(AssertionError):
        early_tr.use_tips(well_list[0])
Beispiel #3
0
def test_tip_tracking_init():
    labware_name = 'opentrons_96_tiprack_300ul'
    labware_def = labware.get_labware_definition(labware_name)
    tiprack = labware.Labware(labware_def,
                              Location(Point(0, 0, 0), 'Test Slot'))
    assert tiprack.is_tiprack
    for well in tiprack.wells():
        assert well.has_tip

    labware_name = 'corning_96_wellplate_360ul_flat'
    labware_def = labware.get_labware_definition(labware_name)
    lw = labware.Labware(labware_def, Location(Point(0, 0, 0), 'Test Slot'))
    assert not lw.is_tiprack
    for well in lw.wells():
        assert not well.has_tip
def test_get_parent_identifier():
    labware_name = 'corning_96_wellplate_360ul_flat'
    labware_def = labware.get_labware_definition(labware_name)
    lw = labware.Labware(labware_def, Location(Point(0, 0, 0), 'Test Slot'))
    # slots have no parent identifier
    assert labware._get_parent_identifier(lw) == ''
    # modules do
    mmg = ModuleGeometry('my magdeck',
                         MagneticModuleModel.MAGNETIC_V1,
                         ModuleType.MAGNETIC,
                         Point(0, 0, 0), 10, 10, Location(Point(1, 2, 3), '3'),
                         APIVersion(2, 4))
    lw = labware.Labware(labware_def, mmg.location)
    assert labware._get_parent_identifier(lw)\
        == MagneticModuleModel.MAGNETIC_V1.value
Beispiel #5
0
def test_load_calibration(monkeypatch, clear_calibration):

    calibration_point = None

    def mock_set_calibration(self, delta):
        nonlocal calibration_point
        calibration_point = delta

    monkeypatch.setattr(labware.Labware, 'set_calibration',
                        mock_set_calibration)

    monkeypatch.setattr(labware, '_hash_labware_def', mock_hash_labware)

    test_labware = labware.Labware(minimalLabwareDef,
                                   Location(Point(0, 0, 0), 'deck'))

    test_offset = Point(1, 1, 1)
    test_tip_length = 31.7

    labware.save_calibration(test_labware, test_offset)
    labware.save_tip_length(test_labware, test_tip_length)

    # Set without saving to show that load will update with previously saved
    # data
    test_labware.set_calibration(Point(0, 0, 0))
    test_labware.tip_length = 46.8

    labware.load_calibration(test_labware)
    assert calibration_point == test_offset
    assert test_labware.tip_length == test_tip_length
Beispiel #6
0
    async def _load_tip_rack_objects(self):
        """
        A function that takes tip rack information
        and loads them onto the deck.
        """
        second_pip = self._get_pipette_by_rank(PipetteRank.second)
        for name, lw_data in self._labware_info.items():
            parent = self._deck.position_for(lw_data.slot)
            lw = labware.Labware(lw_data.definition, parent)
            self._deck[lw_data.slot] = lw

            for mount in lw_data.forMounts:
                is_second_mount = second_pip and second_pip.mount == mount
                pips_share_rack = len(lw_data.forMounts) == 2
                well_name = 'A1'
                if is_second_mount and pips_share_rack:
                    well_name = 'B1'
                well = lw.wells_by_name()[well_name]
                position = well.top().point + MOVE_TO_TIP_RACK_SAFETY_BUFFER
                move = CheckMove(position=position, locationId=uuid4())

                if is_second_mount:
                    self._moves.preparingSecondPipette = move
                else:
                    self._moves.preparingFirstPipette = move
Beispiel #7
0
def test_add_index_file(labware_name, index_file_dir):
    deck = Deck()
    parent = deck.position_for(1)
    definition = labware.get_labware_definition(labware_name)
    lw = labware.Labware(definition, parent)
    labware_hash = labware._hash_labware_def(lw._definition)
    labware._add_to_index_offset_file(lw, labware_hash)

    lw_uri = labware.uri_from_definition(definition)

    str_parent = labware._get_parent_identifier(lw.parent)
    slot = '1'
    if str_parent:
        mod_dict = {str_parent: f'{slot}-{str_parent}'}
    else:
        mod_dict = {}
    blob = {
        "id": f'{labware_hash}',
        "slot": f'{labware_hash}{str_parent}',
        "module": mod_dict
    }

    lw_path = index_file_dir / 'index.json'
    info = labware._read_file(lw_path)
    assert info[lw_uri] == blob
Beispiel #8
0
def test_air_gap():
    m = mock.MagicMock()
    m.pipette_mock = mock.create_autospec(InstrumentContext)
    m.mock_set_flow_rate = mock.MagicMock()

    deck = Location(Point(0, 0, 0), 'deck')
    well_name = 'A2'
    some_labware = labware.Labware(minimalLabwareDef2, deck)
    loaded_labware = {'someLabwareId': some_labware}
    params = {'labware': 'someLabwareId', 'well': well_name}

    params = {'pipette': 'somePipetteId', 'volume': 42,
              'labware': 'someLabwareId', 'well': well_name,
              'offsetFromBottomMm': 12}

    instruments = {'somePipetteId': m.pipette_mock}

    loaded_labware = {'someLabwareId': some_labware}

    with mock.patch(
            'opentrons.protocols.execution.execute_json_v3._set_flow_rate',
            new=m.mock_set_flow_rate):
        _air_gap(instruments, loaded_labware, params)

    # NOTE: air_gap `height` arg is mm from well top,
    # so we expect it to equal offsetFromBottomMm - well depth.
    assert m.mock_calls == [
        mock.call.mock_set_flow_rate(m.pipette_mock, params),
        mock.call.pipette_mock.move_to(
            Location(point=Point(x=19, y=28, z=17.0),
                     labware=some_labware[well_name])),
        mock.call.pipette_mock.air_gap(42, 12 - 40)
    ]
def test_labware_init():
    deck = Location(Point(0, 0, 0), 'deck')
    fake_labware = labware.Labware(minimalLabwareDef, deck)
    ordering = [well for col in minimalLabwareDef['ordering'] for well in col]
    assert fake_labware._ordering == ordering
    assert fake_labware._well_definition == minimalLabwareDef['wells']
    assert fake_labware._offset == Point(x=10, y=10, z=5)
def test_add_index_file(labware_name, labware_offset_tempdir):
    deck = Deck()
    parent = deck.position_for(1)
    definition = labware.get_labware_definition(labware_name)
    lw = labware.Labware(definition, parent)
    labware_hash = helpers.hash_labware_def(definition)
    labware.save_calibration(lw, Point(0, 0, 0))

    lw_uri = helpers.uri_from_definition(definition)

    str_parent = labware._get_parent_identifier(lw)
    slot = '1'
    if str_parent:
        mod_dict = {str_parent: f'{slot}-{str_parent}'}
    else:
        mod_dict = {}
    full_id = f'{labware_hash}{str_parent}'
    blob = {
            "uri": f'{lw_uri}',
            "slot": full_id,
            "module": mod_dict
        }

    lw_path = labware_offset_tempdir / 'index.json'
    info = file_operators.read_cal_file(lw_path)
    assert info['data'][full_id] == blob
Beispiel #11
0
def test_get_location_with_offset():
    deck = Location(Point(0, 0, 0), 'deck')
    some_labware = labware.Labware(minimalLabwareDef2, deck)
    loaded_labware = {'someLabwareId': some_labware}
    params = {'offsetFromBottomMm': 3,
              'labware': 'someLabwareId', 'well': 'A2'}
    result = _get_location_with_offset(loaded_labware, params)
    assert result == Location(Point(19, 28, 8), some_labware['A2'])
def test_get_well():
    deck = Location(Point(0, 0, 0), 'deck')
    well_name = 'A2'
    some_labware = labware.Labware(minimalLabwareDef2, deck)
    loaded_labware = {'someLabwareId': some_labware}
    params = {'labware': 'someLabwareId', 'well': well_name}
    result = _get_well(loaded_labware, params)
    assert result == some_labware[well_name]
Beispiel #13
0
def test_uris():
    details = ('opentrons', 'opentrons_96_tiprack_300ul', '1')
    uri = 'opentrons/opentrons_96_tiprack_300ul/1'
    assert labware.uri_from_details(*details) == uri
    defn = labware.get_labware_definition(details[1], details[0], details[2])
    assert labware.uri_from_definition(defn) == uri
    lw = labware.Labware(defn, Location(Point(0, 0, 0), 'Test Slot'))
    assert lw.uri == uri
def test_wells_rebuilt_with_offset():
    test_labware = labware.Labware(minimalLabwareDef,
                                   Location(Point(0, 0, 0), 'deck'))
    old_wells = test_labware._wells
    assert test_labware._offset == Point(10, 10, 5)
    assert test_labware._calibrated_offset == Point(10, 10, 5)
    labware.save_calibration(test_labware, Point(2, 2, 2))
    new_wells = test_labware._wells
    assert old_wells[0] != new_wells[0]
    assert test_labware._offset == Point(10, 10, 5)
    assert test_labware._calibrated_offset == Point(12, 12, 7)
def test_col_name_accessor():
    deck = Location(Point(0, 0, 0), 'deck')
    fake_labware = labware.Labware(minimalLabwareDef, deck)
    depth1 = minimalLabwareDef['wells']['A1']['depth']
    depth2 = minimalLabwareDef['wells']['A2']['depth']
    x = minimalLabwareDef['wells']['A2']['x']
    y = minimalLabwareDef['wells']['A2']['y']
    offset = fake_labware._offset
    a1 = Point(x=offset[0], y=offset[1], z=offset[2] + depth1)
    a2 = Point(x=offset[0] + x, y=offset[1] + y, z=offset[2] + depth2)
    assert fake_labware.columns_by_name()['1'][0]._position == a1
    assert fake_labware.columns_by_name()['2'][0]._position == a2
Beispiel #16
0
def test_save_tip_length(monkeypatch, clear_calibration):
    assert not os.path.exists(path(MOCK_HASH))

    monkeypatch.setattr(labware, '_hash_labware_def', mock_hash_labware)

    test_labware = labware.Labware(minimalLabwareDef,
                                   Location(Point(0, 0, 0), 'deck'))
    calibrated_length = 22.0
    labware.save_tip_length(test_labware, calibrated_length)
    assert os.path.exists(path(MOCK_HASH))
    with open(path(MOCK_HASH)) as calibration_file:
        data = json.load(calibration_file)
        assert data['tipLength']['length'] == calibrated_length
Beispiel #17
0
def set_up_index_file_temporary_directory(server_temp_directory):
    delete.clear_calibrations()
    deck = Deck()
    labware_list = [
        'nest_96_wellplate_2ml_deep', 'corning_384_wellplate_112ul_flat',
        'geb_96_tiprack_1000ul', 'nest_12_reservoir_15ml',
        'opentrons_96_tiprack_10ul'
    ]
    for idx, name in enumerate(labware_list):
        parent = deck.position_for(idx + 1)
        definition = labware.get_labware_definition(name)
        lw = labware.Labware(definition, parent)
        labware.save_calibration(lw, Point(0, 0, 0))
Beispiel #18
0
def test_get_location_with_offset_fixed_trash():
    deck = Location(Point(0, 0, 0), 'deck')
    trash_labware_def = deepcopy(minimalLabwareDef2)
    trash_labware_def['parameters']['quirks'] = ["fixedTrash"]
    trash_labware = labware.Labware(trash_labware_def, deck)

    loaded_labware = {'someLabwareId': trash_labware}
    params = {'offsetFromBottomMm': 3,
              'labware': 'someLabwareId', 'well': 'A1'}

    result = _get_location_with_offset(loaded_labware, params)

    assert result == Location(Point(10, 28, 45), trash_labware['A1'])
Beispiel #19
0
def test_load_tip_length_calibration_data(monkeypatch, clear_tlc_calibration):
    assert not os.path.exists(tlc_path(PIPETTE_ID))

    monkeypatch.setattr(labware, '_hash_labware_def', mock_hash_labware)

    test_labware = labware.Labware(minimalLabwareDef,
                                   Location(Point(0, 0, 0), 'deck'))
    tip_length = 22.0
    test_data = labware.create_tip_length_data(test_labware, tip_length)
    labware.save_tip_length_calibration(PIPETTE_ID, test_data)
    result = labware.load_tip_length_calibration(PIPETTE_ID, test_labware)

    assert result == test_data[MOCK_HASH]
def set_up_index_file(labware_offset_tempdir):
    deck = Deck()
    labware_list = [
        'nest_96_wellplate_2ml_deep',
        'corning_384_wellplate_112ul_flat',
        'geb_96_tiprack_1000ul',
        'nest_12_reservoir_15ml']
    for idx, name in enumerate(labware_list):
        parent = deck.position_for(idx+1)
        definition = labware.get_labware_definition(name)
        lw = labware.Labware(definition, parent)
        labware.save_calibration(lw, Point(0, 0, 0))

    return labware_list
def test_row_name_accessor():
    deck = Location(Point(0, 0, 0), 'deck')
    fake_labware = labware.Labware(minimalLabwareDef2, deck)
    depth1 = minimalLabwareDef2['wells']['A1']['depth']
    x1 = minimalLabwareDef2['wells']['A1']['x']
    y1 = minimalLabwareDef2['wells']['A1']['y']
    depth2 = minimalLabwareDef2['wells']['B2']['depth']
    x2 = minimalLabwareDef2['wells']['B2']['x']
    y2 = minimalLabwareDef2['wells']['B2']['y']
    offset = fake_labware._offset
    a1 = Point(x=offset[0] + x1, y=offset[1] + y1, z=offset[2] + depth1)
    b2 = Point(x=offset[0] + x2, y=offset[1] + y2, z=offset[2] + depth2)
    assert fake_labware.rows_by_name()['A'][0]._position == a1
    assert fake_labware.rows_by_name()['B'][1]._position == b2
Beispiel #22
0
def test_well_parent():
    labware_name = 'corning_96_wellplate_360ul_flat'
    labware_def = labware.get_labware_definition(labware_name)
    lw = labware.Labware(labware_def, Location(Point(0, 0, 0), 'Test Slot'))
    parent = Location(Point(7, 8, 9), lw)
    well_name = 'circular_well_json'
    has_tip = True
    well = labware.Well(test_data[well_name], parent, well_name, has_tip)
    assert well.parent is lw
    assert well.top().labware is well
    assert well.top().labware.parent is lw
    assert well.bottom().labware is well
    assert well.bottom().labware.parent is lw
    assert well.center().labware is well
    assert well.center().labware.parent is lw
Beispiel #23
0
def test_schema_shape(monkeypatch, clear_calibration):
    def fake_time():
        return 1

    monkeypatch.setattr(time, 'time', fake_time)

    test_labware = labware.Labware(minimalLabwareDef,
                                   Location(Point(0, 0, 0), 'deck'))

    monkeypatch.setattr(labware, '_hash_labware_def', mock_hash_labware)

    labware.save_calibration(test_labware, Point(1, 1, 1))
    expected = {"default": {"offset": [1, 1, 1], "lastModified": 1}}
    with open(path(MOCK_HASH)) as f:
        result = json.load(f)
    assert result == expected
Beispiel #24
0
def test_select_next_tip():
    labware_name = 'opentrons_96_tiprack_300ul'
    labware_def = labware.get_labware_definition(labware_name)
    tiprack = labware.Labware(labware_def,
                              Location(Point(0, 0, 0), 'Test Slot'))
    well_list = tiprack.wells()

    next_one = tiprack.next_tip()
    assert next_one == well_list[0]
    next_five = tiprack.next_tip(5)
    assert next_five == well_list[0]
    next_eight = tiprack.next_tip(8)
    assert next_eight == well_list[0]
    next_nine = tiprack.next_tip(9)
    assert next_nine is None

    # A1 tip only has been used
    tiprack.use_tips(well_list[0])

    next_one = tiprack.next_tip()
    assert next_one == well_list[1]
    next_five = tiprack.next_tip(5)
    assert next_five == well_list[1]
    next_eight = tiprack.next_tip(8)
    assert next_eight == well_list[8]

    # 2nd column has also been used
    tiprack.use_tips(well_list[8], num_channels=8)

    next_one = tiprack.next_tip()
    assert next_one == well_list[1]
    next_five = tiprack.next_tip(5)
    assert next_five == well_list[1]
    next_eight = tiprack.next_tip(8)
    assert next_eight == well_list[16]

    # Bottom 4 tips of 1rd column are also used
    tiprack.use_tips(well_list[4], num_channels=4)

    next_one = tiprack.next_tip()
    assert next_one == well_list[1]
    next_three = tiprack.next_tip(3)
    assert next_three == well_list[1]
    next_five = tiprack.next_tip(5)
    assert next_five == well_list[16]
    next_eight = tiprack.next_tip(8)
    assert next_eight == well_list[16]
Beispiel #25
0
def test_previous_tip():
    labware_name = 'opentrons_96_tiprack_300ul'
    labware_def = labware.get_labware_definition(labware_name)
    tiprack = labware.Labware(labware_def,
                              Location(Point(0, 0, 0), 'Test Slot'))
    # If all wells are used, we can't get a previous tip
    assert tiprack.previous_tip() is None
    # If one well is empty, wherever it is, we can get a slot
    tiprack.wells()[5].has_tip = False
    assert tiprack.previous_tip() is tiprack.wells()[5]
    # But not if we ask for more slots than are available
    assert tiprack.previous_tip(2) is None
    tiprack.wells()[7].has_tip = False
    # And those available wells have to be contiguous
    assert tiprack.previous_tip(2) is None
    # But if they are, we're good
    tiprack.wells()[6].has_tip = False
    assert tiprack.previous_tip(3) is tiprack.wells()[5]
Beispiel #26
0
def test_back_compat():
    labware_name = 'corning_96_wellplate_360ul_flat'
    labware_def = labware.get_labware_definition(labware_name)
    lw = labware.Labware(labware_def, Location(Point(0, 0, 0), 'Test Slot'))

    # Note that this test uses the display name of wells to test for equality,
    # because dimensional parameters could be subject to modification through
    # calibration, whereas here we are testing for "identity" in a way that is
    # related to the combination of well name, labware name, and slot name
    well_a1_name = repr(lw.wells_by_name()['A1'])
    well_b2_name = repr(lw.wells_by_name()['B2'])
    well_c3_name = repr(lw.wells_by_name()['C3'])

    w2 = lw.well(0)
    assert repr(w2) == well_a1_name

    w3 = lw.well('A1')
    assert repr(w3) == well_a1_name

    w4 = lw.wells('B2')
    assert repr(w4[0]) == well_b2_name

    w5 = lw.wells(9, 21, 25, 27)
    assert len(w5) == 4
    assert repr(w5[0]) == well_b2_name

    w6 = lw.wells('A1', 'B2', 'C3')
    assert all([
        repr(well[0]) == well[1]
        for well in zip(w6, [well_a1_name, well_b2_name, well_c3_name])
    ])

    w7 = lw.rows('A')
    assert len(w7) == 1
    assert repr(w7[0][0]) == well_a1_name

    w8 = lw.rows('A', 'C')
    assert len(w8) == 2
    assert repr(w8[0][0]) == well_a1_name
    assert repr(w8[1][2]) == well_c3_name

    w11 = lw.columns('2', '3', '6')
    assert len(w11) == 3
    assert repr(w11[1][2]) == well_c3_name
def test_load_calibration(monkeypatch, clear_calibration):

    monkeypatch.setattr(helpers, 'hash_labware_def', mock_hash_labware)

    test_labware = labware.Labware(minimalLabwareDef,
                                   Location(Point(0, 0, 0), 'deck'))

    test_offset = Point(1, 1, 1)

    labware.save_calibration(test_labware, test_offset)

    # Set without saving to show that load will update with previously saved
    # data
    test_labware.set_calibration(Point(0, 0, 0))
    test_labware.tip_length = 46.8
    lookup_path = labware._get_labware_path(test_labware)
    calibration_point =\
        get.get_labware_calibration(lookup_path, test_labware._definition)
    assert calibration_point == test_offset
Beispiel #28
0
 async def _load_labware_objects(self, **kwargs):
     """
     A function that takes tiprack information and loads them onto the deck.
     """
     full_dict = {}
     for name, data in self._labware_info.items():
         parent = self._deck.position_for(data.slot)
         lw = labware.Labware(data.definition, parent)
         self._deck[data.slot] = lw
         build_dict = {}
         for id in data.forPipettes:
             mount = self._get_mount(id)
             pip = self.get_pipette(mount)
             well_name = 'H1' if pip['channels'] == 8 else 'A1'
             well = lw.wells_by_name()[well_name]
             build_dict[id] = PreparingPipetteMoveOffset(
                 offset=Point(0, 0, 10), well=well
             )
         full_dict[data.id] = build_dict
     self._moves.preparingPipette = full_dict
def test_save_labware_calibration(monkeypatch, clear_calibration):
    # Test the save calibration file
    assert not os.path.exists(path(MOCK_HASH))
    calibration_point = None

    def mock_set_calibration(self, delta):
        nonlocal calibration_point
        calibration_point = delta

    monkeypatch.setattr(labware.Labware, 'set_calibration',
                        mock_set_calibration)

    monkeypatch.setattr(helpers, 'hash_labware_def', mock_hash_labware)

    test_labware = labware.Labware(minimalLabwareDef,
                                   Location(Point(0, 0, 0), 'deck'))

    labware.save_calibration(test_labware, Point(1, 1, 1))
    assert os.path.exists(path(MOCK_HASH))
    assert calibration_point == Point(1, 1, 1)
Beispiel #30
0
def test_schema_shape(monkeypatch, clear_calibration):
    fake_time = utc_now()
    time_string = fake_time.isoformat()
    from_iso = datetime.datetime.fromisoformat(time_string)

    class fake_datetime:
        @classmethod
        def fromisoformat(cls, obj):
            return from_iso

        @classmethod
        def isoformat(cls):
            return time_string

    mock = Mock(spec=fake_datetime)
    mock.__class__ = datetime.datetime

    test_labware = labware.Labware(minimalLabwareDef,
                                   Location(Point(0, 0, 0), 'deck'))

    monkeypatch.setattr(
       helpers,
       'hash_labware_def', mock_hash_labware
    )

    expected = {"default": {"offset": [1, 1, 1], "lastModified": fake_time}}

    def fake_helper_data(path, delta):
        return expected

    monkeypatch.setattr(
        modify,
        '_helper_offset_data_format',
        fake_helper_data
    )

    labware.save_calibration(test_labware, Point(1, 1, 1))
    with open(path(MOCK_HASH)) as f:
        result = json.load(f, cls=ed.DateTimeDecoder)
    assert result == expected