pycall.rb icon indicating copy to clipboard operation
pycall.rb copied to clipboard

Missing wrappers for iterators and generators

Open mrkn opened this issue 7 years ago • 4 comments

I want to write the following Python code

for item in collection:
  ...

like the following in Ruby with PyCall:

collection.each do |item|
  ...
end

Now PyCall defines each methods in PyCall::List and PyCall::Dict in specific ways. It needs to be rewritten in more generic way.

mrkn avatar Sep 14 '17 03:09 mrkn

One candidate is PyCall.enum(obj) to get an Enumerator object.

This is a concept implementation:

module PyCall
  module_function

  def enum(obj)
    gen = obj.__iter__
    Enumerator.new do |y|
      begin
        yield gen.__next__ while true
        obj
      rescue PyError, err
        # ignore StopIteration exception
        return obj if err.type.match?(LibPython::API::PyExc_StopIteration)
        raise
      end
    end
  end
end

mrkn avatar Sep 14 '17 04:09 mrkn

Just one note: I needed to use generator using following function PyCall.builtins.next(generator)

allomov avatar Aug 08 '19 20:08 allomov

Here is my version of the patch:

module PyCall
  module_function

  def enum(generator)
    Enumerator.new do |enum|
      begin
        loop do
          enum << PyCall.builtins.next(generator)
        end
      rescue PyError => error
        raise err unless error.type.to_s.match?("StopIteration")
      end
    end
  end
end

allomov avatar Aug 08 '19 22:08 allomov

@allomov Could you please make a pull-request?

mrkn avatar Aug 19 '19 03:08 mrkn