0

I would like to access a Python package in Ruby using PyCall.

The non-blocking function in the Python code below activates a callback.

Could you help me implement the callback?

#!/usr/bin/env python
# start_reading.py
import time
import mercury

def found(tag):
    print(tag.epc.decode()) # Check check
    # print(tags[1].epc.decode())

reader = mercury.Reader("tmr:///dev/ttyUSB0")

reader.start_reading(found, on_time=250, off_time=0)
time.sleep(0.5)
reader.stop_reading()

Output start_reading.py:

b'001853000000000000000012'
b'34155CACB400000000000125'
b'34155CACB400000000000125'
b'34155CACB400000000000125'
b'001853000000000000000012'
b'34155CACB400000000000125'
b'001853000000000000000012'
b'34155CACB400000000000125'
b'34155CACB400000000000125'

This pertains an api for ThingMagic RFID readers, which is a Python wrapper for the original api written in C.

From the docs:

reader.start_reading(callback, on_time=250, off_time=0)

Starts asynchronous reading. It returns immediately and begins a sequence of reads or a continuous read. The results are passed to the callback. The reads are repeated until the reader.stop_reading() method is called

  • callback(TagReadData) will be invoked for every tag detected
  • on_time sets the duration, in milliseconds, for the reader to be actively querying
  • off_time duration, in milliseconds, for the reader to be quiet while querying

As follows the parts I got working using a single read function. First the Python code, then Ruby.

#!/usr/bin/env python
# read.py
import mercury
reader = mercury.Reader("tmr:///dev/ttyUSB0")

data = reader.read()
print(data)
print(f"type: {type(data)}")

Output read.py:

[EPC(b'34155CACB400000000000125'), EPC(b'001853000000000000000012')]
type: <class 'list'>
#!/usr/bin/env ruby
# read.rb
require 'pycall/import'
include PyCall::Import
pyimport :mercury

reader = mercury.Reader.new("tmr:///dev/ttyUSB0")

python_data = reader.read() 
puts "python_data: #{python_data}"
puts "python class: #{python_data.class}"
puts ""
ruby_data = python_data.to_ary
puts "ruby_data: #{ruby_data}"
puts "ruby class: #{ruby_data.class}"

Output read.rb:

python_data: [EPC(b'34155CACB400000000000125'), EPC(b'001853000000000000000012')]
python class: <class 'list'>

ruby_data: [EPC(b'34155CACB400000000000125'), EPC(b'001853000000000000000012')]
ruby class: Array

The above works, as you can see.

The solution for the callback should be an improvement upon the following code.

#!/usr/bin/env ruby
# start_reading.rb
require 'pycall/import'
include PyCall::Import
pyimport :mercury

def found(tag)
  puts tag.epc
end

reader = mercury.Reader.new("tmr:///dev/ttyUSB0")

reader.start_reading(found, on_time=250, off_time=0) # What should I do here?
sleep(0.5)
reader.stop_reading()

Current output start_reading.rb

start_reading.rb:7:in `found': wrong number of arguments (given 0, expected 1) (ArgumentError)
    from start_reading.rb:14:in `<main>'

EDIT - for clarification.

I think reader.start_reading() should receive something Python understands, right now it receives Ruby code.

Hence, the 'found' function (or lambda) and the variables need to be translated from Ruby (Ru) to Python (Py) using PyCall.

I showed I can use PyCall to call the Py Reader object from Ru and translate the output from list to array. So the first and last step are done, great. But every interaction in between has to be bridged with PyCall as well (i.e. the 'found' func. from Ru to Py).

That, I think, is the challenge here.

5
  • The example there in the docs is reader.start_reading(lambda tag: print(tag.epc)) - have you tried the same thing? reader.start_reading(lambda tag: found(tag), on_time=250, off_time=0) Commented Nov 27, 2025 at 11:43
  • 1
    if you need a lambda it should be: reader.start_reading(->(tag){ puts tag.epc }, 250, 0) or reader.start_reading(method(:found), 250, 0) Commented Nov 28, 2025 at 3:42
  • Thank you for your responses. I tried both lambda's, which don't not work, both offer the following response: .../pycall/pytypeobject_wrapper.rb:24:in `call_object': <class 'TypeError'>: Resource temporarily unavailable (PyCall::PyError). I think whatever is passed to the reader.start_reading() function should be something Python understands. Hence, I think I need a way to transform the found function (or the lambda) from Ruby to Python using PyCall - the same is true for the other two variables (which are optional so I omitted them in my test). Commented Nov 28, 2025 at 11:22
  • maybe asynchronous version is just not supported github.com/red-data-tools/… Commented Nov 29, 2025 at 4:06
  • The fact that PyCall is not Thread safe is a separate issue. I think it means one can not initiate a Python object in the main Ruby thread and then use it in a Ruby sub-thread. Commented Dec 1, 2025 at 11:15

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.