Skip to content

Commit eaec5bc

Browse files
authored
Merge pull request #148 from mrkn/iterable_wrapper
Add PyCall.iterable
2 parents 9f06f50 + bef15b8 commit eaec5bc

File tree

6 files changed

+76
-0
lines changed

6 files changed

+76
-0
lines changed

lib/pycall.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module PyCall
55
require 'pycall/pyobject_wrapper'
66
require 'pycall/pytypeobject_wrapper'
77
require 'pycall/pymodule_wrapper'
8+
require 'pycall/iterable_wrapper'
89
require 'pycall/init'
910

1011
module_function
@@ -73,6 +74,10 @@ def import_module(name)
7374
LibPython::Helpers.import_module(name)
7475
end
7576

77+
def iterable(obj)
78+
IterableWrapper.new(obj)
79+
end
80+
7681
def len(obj)
7782
case obj
7883
when PyObjectWrapper

lib/pycall/iterable_wrapper.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module PyCall
2+
class IterableWrapper
3+
include Enumerable
4+
5+
def initialize(obj)
6+
@obj = check_iterable(obj)
7+
end
8+
9+
private def check_iterable(obj)
10+
unless PyCall.hasattr?(obj, :__iter__)
11+
raise ArgumentError, "%p object is not iterable" % obj
12+
end
13+
obj
14+
end
15+
16+
def each
17+
return enum_for(__method__) unless block_given?
18+
iter = @obj.__iter__()
19+
while true
20+
begin
21+
yield iter.__next__()
22+
rescue PyCall::PyError => err
23+
if err.type == PyCall.builtins.StopIteration
24+
break
25+
else
26+
raise err
27+
end
28+
end
29+
end
30+
end
31+
end
32+
end

test/helper.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
require "pycall"
2+
require "pathname"
23
require "test-unit"
4+
5+
test_dir = Pathname.new(__dir__)
6+
python_dir = test_dir + "python"
7+
8+
PyCall.sys.path.append(python_dir.to_s)

test/python/pycall/__init__.py

Whitespace-only changes.

test/python/pycall/simple_iterable.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class IntGenerator(object):
2+
def __init__(self, start, stop):
3+
self.start = start
4+
self.stop = stop
5+
self.current = None
6+
7+
def __iter__(self):
8+
self.current = self.start
9+
return self
10+
11+
def __next__(self):
12+
if self.current == self.stop:
13+
raise StopIteration()
14+
value = self.current
15+
self.current += 1
16+
return value
17+
18+
if __name__ == "__main__":
19+
print(list(IntGenerator(105, 115)))

test/test-pycall.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,18 @@ class PyCallTest < Test::Unit::TestCase
22
def test_VERSION
33
assert_not_nil(PyCall::VERSION)
44
end
5+
6+
def test_iterable
7+
simple_iterable = PyCall.import_module("pycall.simple_iterable")
8+
int_gen = simple_iterable.IntGenerator.new(10, 25)
9+
iterable = PyCall.iterable(int_gen)
10+
assert_equal({
11+
enumerable_p: true,
12+
each_to_a: (10 .. 24).to_a,
13+
},
14+
{
15+
enumerable_p: iterable.is_a?(Enumerable),
16+
each_to_a: iterable.each.to_a
17+
})
18+
end
519
end

0 commit comments

Comments
 (0)