Error in first run PyLabRobot on OT-2

Hi,

I am trying to run PyLabRobot for the first time to connect/operate an Opentrons OT-2. I am getting an error:

RuntimeError: Opentrons is not installed. Please run pip install pylabrobot[opentrons]. Only supported on Python 3.10 and below.

I can ping the opentrons from the terminal. I have run the pip install for those packages. python --version shows I am running Python 3.10.12.

Also, what is this message an indicator of? Is it not getting a connection to the IP address? Is it some package/dependency is not present or something to do with the Python version?

Let me know what other commands I can run and output I can post…

PING 192.168.1.121 (192.168.1.121) 56(84) bytes of data.
64 bytes from 192.168.1.121: icmp_seq=1 ttl=64 time=5.56 ms
64 bytes from 192.168.1.121: icmp_seq=2 ttl=64 time=25.4 ms
64 bytes from 192.168.1.121: icmp_seq=3 ttl=64 time=4.88 ms
64 bytes from 192.168.1.121: icmp_seq=4 ttl=64 time=4.86 ms
64 bytes from 192.168.1.121: icmp_seq=5 ttl=64 time=4.84 ms
64 bytes from 192.168.1.121: icmp_seq=6 ttl=64 time=4.99 ms
64 bytes from 192.168.1.121: icmp_seq=7 ttl=64 time=9.98 ms
64 bytes from 192.168.1.121: icmp_seq=8 ttl=64 time=5.63 ms
^C
— 192.168.1.121 ping statistics —
8 packets transmitted, 8 received, 0% packet loss, time 7009ms
rtt min/avg/max/mdev = 4.836/8.266/25.405/6.675 ms
(env) test@ubuntu:~/Documents$ python opentrons.py
Traceback (most recent call last):
File “/home/test/Documents/opentrons.py”, line 6, in
ot = OpentronsBackend(host=“192.168.1.121”, port=5001)
File “/home/test/pylabrobot/pylabrobot/liquid_handling/backends/opentrons_backend.py”, line 71, in init
raise RuntimeError(“Opentrons is not installed. Please run pip install pylabrobot[opentrons].”
RuntimeError: Opentrons is not installed. Please run pip install pylabrobot[opentrons]. Only supported on Python 3.10 and below.
(env) test@ubuntu:~/Documents$ python --version
Python 3.10.12
(env) test@ubuntu:~/Documents$

Below is my code:

from pylabrobot.liquid_handling import LiquidHandler

from pylabrobot.liquid_handling.backends.opentrons_backend import OpentronsBackend
from pylabrobot.resources.opentrons import OTDeck

ot = OpentronsBackend(host=“192.168.1.121”, port=5001)
lh = LiquidHandler(backend=ot, deck=OTDeck())
lh.setup()

#Deck setup
from pylabrobot.resources.opentrons import (
opentrons_96_filtertiprack_20ul
)

tips = opentrons_96_filtertiprack_20ul(name=“tip_rack”)

lh.deck.assign_child_resource(tips, slot=1)

#Liquid handling protocol
lh.pickup_tips(tips[“H6”])
lh.discard_tips(tips[“H6”])

1 Like

I would suggest not having a python script with the same name as a library (e.g. opentrons.py), this can cause import errors down the line, although probably not the one you are seeing.

Not sure why the extras install (the part of pip install inside the brackets) didn’t work, but if you run pip install opentrons-http-api-client I think this will fix the issue.

Setup.py gives this as the extras keyword for the opentrons install, so maybe the install instruction is out of date:

extras_opentrons = [
  "opentrons-http-api-client",
  "opentrons-shared-data"
]

e.g.

pip install pylabrobot[extras_opentrons]

@MikeMueller, where did you find this IP?

You should go to the Opentrons app > robot settings > networking > ip.

Make sure that the computer you’re using this from and the OT are on the same network, and that p2p is allowed on that network.

To verify this is indeed the OT, go to http://<ip>:31950. The docs should load there.

Another thing to verify is you’re running Python 3.10 locally. What happens when you run import ot_api in a Python repl?

The port here should be 31950, not 5001, most likely.

I don’t think the installation instruction is out of date.

This is wrong. It should be pip install pylabrobot[opentrons]

Thanks for the assistance Rick and Stefan…

So kind of interesting, perhaps one step forward and one step back. I changed the file name as suggested to opentrons-test.py and also altered the port to 31950. Initially, it seems to have some success. At least it got further along, until it didn’t like the “slot” in the later line of code. But in running it again further, now it’s back with the same line again as my original message, which to me thinks maybe there is some kind of connection issue? Please see the output I paste below…

===== One step forward =================

/home/test/Documents/opentrons_test.py:9: RuntimeWarning: coroutine ‘LiquidHandler.setup’ was never awaited
lh.setup()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Traceback (most recent call last):
File “/home/test/Documents/opentrons_test.py”, line 18, in
lh.deck.assign_child_resource(tips, slot=1)
TypeError: OTDeck.assign_child_resource() got an unexpected keyword argument ‘slot’

====== One step back =====================

(env) test@ubuntu:~/Documents$ python opentrons_test.py
Traceback (most recent call last):
File “/home/test/Documents/opentrons_test.py”, line 6, in
ot = OpentronsBackend(host=“192.168.1.121”, port=31950)
File “/home/test/pylabrobot/pylabrobot/liquid_handling/backends/opentrons_backend.py”, line 71, in init
raise RuntimeError(“Opentrons is not installed. Please run pip install pylabrobot[opentrons].”
RuntimeError: Opentrons is not installed. Please run pip install pylabrobot[opentrons]. Only supported on Python 3.10 and below.

for issue 1, use assign_child_at_slot instead of assign_child_resource.

for issue 2, this is an import issue, not a connection issue. Can you share the outputs of python --version and python -c 'import ot_api'?

Hi Rick,

(env) test@ubuntu:~/Documents$ python --version
Python 3.10.12
(env) test@ubuntu:~/Documents$ python -c ‘import ot_api’
/home/test/Documents/opentrons.py:8: RuntimeWarning: coroutine ‘LiquidHandler.setup’ was never awaited
lh.setup()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Traceback (most recent call last):
File “”, line 1, in
File “/home/test/pylabrobot/env/lib/python3.10/site-packages/ot_api/init.py”, line 19, in
import ot_api.labware as labware
File “/home/test/pylabrobot/env/lib/python3.10/site-packages/ot_api/labware.py”, line 5, in
from ot_api.decorators import request_with_run_id, command
File “/home/test/pylabrobot/env/lib/python3.10/site-packages/ot_api/decorators.py”, line 6, in
import opentrons.protocol_engine.errors as ot_errors
File “/home/test/Documents/opentrons.py”, line 17, in
lh.deck.assign_child_resource(tips, slot=1)
TypeError: OTDeck.assign_child_resource() got an unexpected keyword argument ‘slot’
(env) test@ubuntu:~/Documents$

ah, it seems there still is a file named opentrons somewhere that is imported instead of the real opentrons library. import ot_api imports from here: /home/test/Documents/opentrons.py

(working on a more robust check for checking whether opentrons is installed. Currently we just import opentrons and if that fails we tell the user OT is not installed. This hides issues like yours. WIP.)

1 Like

Hi Rick, I think I’m pretty close to having this working, just running a simple script to pick up a tip. I’m getting a key error related to the tip rack which is preventing tip pickup. It is placing it on the worktable correctly though there is a traceback message showing related to the slot ahead of that.

Below is my code:
import asyncio

from pylabrobot.liquid_handling import LiquidHandler

from pylabrobot.liquid_handling.backends.opentrons_backend import OpentronsBackend
from pylabrobot.resources.opentrons import OTDeck

ot = OpentronsBackend(host=“192.168.1.121”, port=31950)
lh = LiquidHandler(backend=ot, deck=OTDeck())
async def liquid_handler_setup():
await lh.setup()

asyncio.run(liquid_handler_setup())

#Deck setup
from pylabrobot.resources.opentrons import (
opentrons_96_filtertiprack_20ul
)

tips1 = opentrons_96_filtertiprack_20ul(name=“tip_rack”)

lh.deck.assign_child_at_slot(tips1, slot=1)

print(lh.deck.summary())

#Liquid handling protocol

async def the_liquid_handling_protocol():
await lh.pick_up_tips(tips1[“H6”])

asyncio.run(the_liquid_handling_protocol())

Below is the output:

test@ubuntu:~/Documents$ python opentrons_test.py
Exception in thread Thread-2 (callback):
Traceback (most recent call last):
File “/usr/lib/python3.10/threading.py”, line 1016, in _bootstrap_inner
self.run()
File “/usr/lib/python3.10/threading.py”, line 953, in run
self._target(*self._args, **self._kwargs)
File “/usr/local/lib/python3.10/dist-packages/pylabrobot/liquid_handling/liquid_handler.py”, line 167, in callback
loop.run_until_complete(func(*args, **kwargs))
File “/usr/lib/python3.10/asyncio/base_events.py”, line 649, in run_until_complete
return future.result()
File “/usr/local/lib/python3.10/dist-packages/pylabrobot/liquid_handling/backends/opentrons_backend.py”, line 246, in assigned_resource_callback
ot_api.labware.add(
File “/usr/local/lib/python3.10/dist-packages/ot_api/decorators.py”, line 28, in wrapper
raise e
File “/usr/local/lib/python3.10/dist-packages/ot_api/decorators.py”, line 24, in wrapper
return f(*args, **kwargs)
File “/usr/local/lib/python3.10/dist-packages/ot_api/decorators.py”, line 41, in wrapper
command_id = f(*args, **kwargs)
TypeError: add() got an unexpected keyword argument ‘slot’
Exception in thread Thread-3 (callback):
Traceback (most recent call last):
File “/usr/lib/python3.10/threading.py”, line 1016, in _bootstrap_inner
self.run()
File “/usr/lib/python3.10/threading.py”, line 953, in run
self._target(*self._args, **self._kwargs)
File “/usr/local/lib/python3.10/dist-packages/pylabrobot/liquid_handling/liquid_handler.py”, line 167, in callback
loop.run_until_complete(func(*args, **kwargs))
File “/usr/lib/python3.10/asyncio/base_events.py”, line 649, in run_until_complete
return future.result()
File “/usr/local/lib/python3.10/dist-packages/pylabrobot/liquid_handling/backends/opentrons_backend.py”, line 246, in assigned_resource_callback
ot_api.labware.add(
File “/usr/local/lib/python3.10/dist-packages/ot_api/decorators.py”, line 28, in wrapper
raise e
File “/usr/local/lib/python3.10/dist-packages/ot_api/decorators.py”, line 24, in wrapper
return f(*args, **kwargs)
File “/usr/local/lib/python3.10/dist-packages/ot_api/decorators.py”, line 41, in wrapper
command_id = f(*args, **kwargs)
TypeError: add() got an unexpected keyword argument ‘slot’

Deck: 624.3mm x 565.2mm

±----------------±----------------±----------------+
| | | |
| 10: Empty | 11: Empty | 12: trash_co… |
| | | |
±----------------±----------------±----------------+
| | | |
| 7: Empty | 8: Empty | 9: Empty |
| | | |
±----------------±----------------±----------------+
| | | |
| 4: Empty | 5: Empty | 6: Empty |
| | | |
±----------------±----------------±----------------+
| | | |
| 1: tip_rack | 2: Empty | 3: Empty |
| | | |
±----------------±----------------±----------------+

Traceback (most recent call last):
File “/home/test/Documents/opentrons_test.py”, line 31, in
asyncio.run(the_liquid_handling_protocol())
File “/usr/lib/python3.10/asyncio/runners.py”, line 44, in run
return loop.run_until_complete(main)
File “/usr/lib/python3.10/asyncio/base_events.py”, line 649, in run_until_complete
return future.result()
File “/home/test/Documents/opentrons_test.py”, line 29, in the_liquid_handling_protocol
await lh.pick_up_tips(tips1[“H6”])
File “/usr/local/lib/python3.10/dist-packages/pylabrobot/machine.py”, line 23, in wrapper
return await func(self, *args, **kwargs)
File “/usr/local/lib/python3.10/dist-packages/pylabrobot/liquid_handling/liquid_handler.py”, line 380, in pick_up_tips
self._trigger_callback(
File “/usr/local/lib/python3.10/dist-packages/pylabrobot/liquid_handling/liquid_handler.py”, line 1638, in _trigger_callback
raise error
File “/usr/local/lib/python3.10/dist-packages/pylabrobot/liquid_handling/liquid_handler.py”, line 374, in pick_up_tips
await self.backend.pick_up_tips(ops=pickups, use_channels=use_channels, **backend_kwargs)
File “/usr/local/lib/python3.10/dist-packages/pylabrobot/liquid_handling/backends/opentrons_backend.py”, line 301, in pick_up_tips
labware_id = self.defined_labware[op.resource.parent.name] # get name of tip rack
KeyError: ‘tip_rack’

This is the real source of the error. The reason you get the KeyError later is that the resource fails to get assigned to the deck.

The OT HTTP Python API wrapper defines the add method which now takes ot_location instead of slot: opentrons-python-api/ot_api/labware.py at main · rickwierenga/opentrons-python-api · GitHub (it took slot before).

Are you running the latest version of PLR from github? The Opentrons backend should use the ot_location arg: pylabrobot/pylabrobot/liquid_handling/backends/opentrons_backend.py at main · PyLabRobot/pylabrobot · GitHub

Hi Rick,

So I made that update, but still seeing issues with it. So I’ve tried some simpler code based on what you have on your API page:

When I tried to do add, it wanted both the namespace and version in the fields, so for now I set them to None, which may be problematic. Most examples I’ve seen don’t have those specified at all. But where it seems to be trip up now is in finding the tip definition, it’s like the name isn’t matching something in the database. I’m trying to use a 20 uL filter tip box. i’ve found the name in the opentrons-shared-data, I’ve even tried specifying the path all the way to the .json.

labware_def = ot_api.labware.define(“opentrons_96_filtertiprack_20ul”)
ot_api.labware.add(labware_def, namespace=None, version=None, ot_location=1)

test@ubuntu:~/Documents$ python test3.py
Traceback (most recent call last):
File “/home/test/Documents/test3.py”, line 20, in
labware_def = ot_api.labware.define(“opentrons_shared_data/data/labware/definitions/2/opentrons_96_filtertiprack_20ul/1.json”) # json from opentrons-shared-data
File “/usr/local/lib/python3.10/dist-packages/ot_api/decorators.py”, line 24, in wrapper
return f(*args, **kwargs)
File “/usr/local/lib/python3.10/dist-packages/ot_api/labware.py”, line 14, in define
return ot_api.requestor.post(f"/runs/{run_id}/labware_definitions", data)
File “/usr/local/lib/python3.10/dist-packages/ot_api/requestor.py”, line 38, in post
return _return_resp(resp)
File “/usr/local/lib/python3.10/dist-packages/ot_api/requestor.py”, line 24, in _return_resp
raise Exception(resp.text)
Exception: {“errors”:[{“id”:“InvalidRequest”,“title”:“Invalid Request”,“detail”:“value is not a valid dict”,“source”:{“pointer”:“/data”},“errorCode”:“4000”}]}

Do you need to this “manually” with the ot_api? PLR does this automatically. Note how the argument passed to .add is not the return value of define:

data = ot_api.labware.define(lw)
namespace, definition, version = data["data"]["definitionUri"].split("/")

# assign labware to robot
labware_uuid = resource.name

ot_api.labware.add(
  load_name=definition,
  namespace=namespace,
  ot_location=ot_location,
  version=version,
  labware_id=labware_uuid,
  display_name=resource.name)

Here, lw is a dictionary that can be loaded from shared_data. In PLR, I dynamically create an OT-style definition:

lw = {
 "schemaVersion": 2,
  "version": 1,
  ...
  "dimensions":{
    "xDimension": resource.get_size_x(),
    "yDimension": resource.get_size_y(),
    "zDimension": resource.get_size_z(),
  }
  ...
}

There is no database to be matched against, it’s just a definition of the tip rack: location of A1, size, etc.

Oops, realized the docs for the python http api are wrong. fixed: Update README.md · rickwierenga/opentrons-python-api@28547fc · GitHub