Skip to content

Commit 30e6944

Browse files
committed
make our interop documentation doctest executable
1 parent 9f153cc commit 30e6944

File tree

3 files changed

+115
-73
lines changed

3 files changed

+115
-73
lines changed

docs/user/Interoperability.md

Lines changed: 82 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -14,77 +14,86 @@ In fact, GraalVM uses this API internally to execute Python C extensions using t
1414

1515
You can import the `polyglot` module to interact with other languages:
1616
```python
17-
import polyglot
17+
>>> import polyglot
18+
```
19+
20+
You can evaluate some inlined code from another language:
21+
```python
22+
>>> polyglot.eval(string="1 + 1", language="ruby")
23+
2
24+
```
25+
26+
You can evaluate some code from a file, by passing the path to it:
27+
```python
28+
>>> with open("./my_ruby_file.rb", "w") as f:
29+
... f.write("Polyglot.export('RubyPolyglot', Polyglot)")
30+
41
31+
>>> polyglot.eval(path="./my_ruby_file.rb", language="ruby")
32+
<foreign object at ...>
1833
```
1934

2035
You can import a global value from the entire polyglot scope:
2136
```python
22-
imported_polyglot_global = polyglot.import_value("global_name")
37+
>>> ruby_polyglot = polyglot.import_value("RubyPolyglot")
2338
```
2439

2540
This global value should then work as expected:
2641
* Accessing attributes assumes it reads from the `members` namespace.
27-
* Accessing items is supported both with strings and numbers.
28-
* Calling methods on the result tries to do a straight invoke and falls
29-
back to reading the member and trying to execute it.
30-
31-
You can evaluate some inlined code from another language:
3242
```python
33-
polyglot.eval(string="1 + 1", language="ruby")
43+
>>> ruby_polyglot.to_s
44+
<foreign object at ...>
3445
```
3546

36-
You can evaluate some code from a file, by passing the path to it:
47+
* Calling methods on the result tries to do a straight invoke and falls
48+
back to reading the member and trying to execute it.
3749
```python
38-
polyglot.eval(path="./my_ruby_file.rb", language="ruby")
50+
>>> ruby_polyglot.to_s()
51+
Polyglot
3952
```
4053

41-
If you pass a file, you can also rely on the file-based language detection:
54+
* Accessing items is supported both with strings and numbers.
4255
```python
43-
polyglot.eval(path="./my_ruby_file.rb")
56+
>>> ruby_polyglot.methods()[10] is not None
57+
True
4458
```
4559

46-
You can export some oblect from Python to other supported languages so they can import
60+
You can export some object from Python to other supported languages so they can import
4761
it:
4862
```python
49-
foo = object()
50-
polyglot.export_value(foo, name="python_foo")
63+
>>> foo = object()
64+
>>> polyglot.export_value(value=foo, name="python_foo")
65+
<object object at ...>
66+
>>> jsfoo = polyglot.eval(language="js", string="Polyglot.import('python_foo')")
67+
>>> jsfoo is foo
68+
True
5169
```
5270

5371
The export function can be used as a decorator.
5472
In this case the function name is used as the globally exported name:
5573
```python
56-
@polyglot.export_value
57-
def python_method():
58-
return "Hello from Python!"
74+
>>> @polyglot.export_value
75+
... def python_method():
76+
... return "Hello from Python!"
5977
```
6078

6179
Here is an example of how to use the JavaScript regular expression engine to
62-
match Python strings. Save this code to the `polyglot_example.py` file:
80+
match Python strings.
6381
```python
64-
import polyglot
82+
>>> js_re = polyglot.eval(string="RegExp()", language="js")
6583

66-
re = polyglot.eval(string="RegExp()", language="js")
84+
>>> pattern = js_re.compile(".*(?:we have (?:a )?matching strings?(?:[!\\?] )?)(.*)")
6785

68-
pattern = re.compile(".*(?:we have (?:a )?matching strings?(?:[!\\?] )?)(.*)")
86+
>>> if pattern.exec("This string does not match"):
87+
... raise SystemError("that shouldn't happen")
6988

70-
if pattern.exec("This string does not match"):
71-
raise SystemError("that shouldn't happen")
89+
>>> md = pattern.exec("Look, we have matching strings! This string was matched by Graal.js")
7290

73-
md = pattern.exec("Look, we have matching strings! This string was matched by Graal.js")
74-
if not md:
75-
raise SystemError("this should have matched")
76-
77-
print("Here is what we found: '%s'" % md[1])
78-
```
79-
80-
To run it, pass the `--jvm --polyglot` option to the `graalpy` launcher:
81-
```shell
82-
graalpy --jvm --polyglot polyglot_example.py
91+
>>> "Here is what we found: '%s'" % md[1]
92+
"Here is what we found: 'This string was matched by Graal.js'"
8393
```
8494

8595
This program matches Python strings using the JavaScript regular expression object.
86-
Python reads the captured group from the JavaScript result and prints:
87-
*Here is what we found: 'This string was matched by Graal.js'*.
96+
Python reads the captured group from the JavaScript result and prints it.
8897

8998
As a more complex example, see how you can read a file using R, process the data in Python, and use R again to display the resulting data image, using both the R and Python libraries in conjunction.
9099
To run this example, first install the required R library:
@@ -135,31 +144,36 @@ time.sleep(10)
135144

136145
Finally, to interoperate with Java (only when running on the JVM), you can use the `java` module:
137146
```python
138-
import java
139-
BigInteger = java.type("java.math.BigInteger")
140-
myBigInt = BigInteger.valueOf(42)
141-
# public Java methods can just be called
142-
myBigInt.shiftLeft(128)
143-
# Java method names that are keywords in Python can be accessed using `getattr`
144-
getattr(myBigInt, "not")()
145-
byteArray = myBigInt.toByteArray()
146-
# Java arrays can act like Python lists
147-
print(list(byteArray))
147+
>>> import java
148+
>>> BigInteger = java.type("java.math.BigInteger")
149+
>>> myBigInt = BigInteger.valueOf(42)
150+
>>> # public Java methods can just be called
151+
>>> myBigInt.shiftLeft(128)
152+
<JavaObject[java.math.BigInteger] at ...>
153+
>>> # Java method names that are keywords in Python can be accessed using `getattr`
154+
>>> getattr(myBigInt, "not")()
155+
<JavaObject[java.math.BigInteger] at ...>
156+
>>> byteArray = myBigInt.toByteArray()
157+
>>> # Java arrays can act like Python lists
158+
>>> list(byteArray)
159+
[42]
148160
```
149161

150162
For packages under the `java` package, you can also use the normal Python import
151163
syntax:
152164
```python
153-
import java.util.ArrayList
154-
from java.util import ArrayList
155-
156-
java.util.ArrayList == ArrayList
157-
158-
al = ArrayList()
159-
al.add(1)
160-
al.add(12)
161-
print(al)
162-
# prints [1, 12]
165+
>>> import java.util.ArrayList
166+
>>> from java.util import ArrayList
167+
>>>
168+
>>> java.util.ArrayList == ArrayList
169+
True
170+
>>> al = ArrayList()
171+
>>> al.add(1)
172+
True
173+
>>> al.add(12)
174+
True
175+
>>> al
176+
[1, 12]
163177
```
164178

165179
In addition to the `type` builtin method, the `java` module exposes the following
@@ -173,19 +187,18 @@ Builtin | Specification
173187
`is_symbol(obj)` | returns `True` if `obj` if the argument is a Java host symbol, representing the constructor and static members of a Java class, as obtained by `java.type`
174188

175189
```python
176-
import java
177-
ArrayList = java.type('java.util.ArrayList')
178-
my_list = ArrayList()
179-
print(java.is_symbol(ArrayList))
180-
# prints True
181-
print(java.is_symbol(my_list))
182-
# prints False, my_list is not a Java host symbol
183-
print(java.is_object(ArrayList))
184-
# prints True, symbols are also host objects
185-
print(java.is_function(my_list.add))
186-
# prints True, the add method of ArrayList
187-
print(java.instanceof(my_list, ArrayList))
188-
# prints True
190+
>>> ArrayList = java.type('java.util.ArrayList')
191+
>>> my_list = ArrayList()
192+
>>> java.is_symbol(ArrayList)
193+
True
194+
>>> java.is_symbol(my_list)
195+
False
196+
>>> java.is_object(ArrayList)
197+
True
198+
>>> java.is_function(my_list.add)
199+
True
200+
>>> java.instanceof(my_list, ArrayList)
201+
True
189202
```
190203

191204
See [Polyglot Programming](https://github.com/oracle/graal/blob/master/docs/reference-manual/polyglot-programming.md) and [Embed Languages](https://github.com/oracle/graal/blob/master/docs/reference-manual/embedding/embed-languages.md) for more information about interoperability with other programming languages.

graalpython/com.oracle.graal.python.test/src/graalpytest.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -788,11 +788,15 @@ def decorator(f):
788788
return decorator
789789

790790

791-
class TextTestResult():
791+
class TextTestResult :
792792
"Just a dummy to satisfy the unittest.support import"
793793
pass
794794

795795

796+
class TestSuite:
797+
pass
798+
799+
796800
if __name__ == "__main__":
797801
sys.modules["unittest"] = sys.modules["__main__"]
798802
patterns = []

graalpython/com.oracle.graal.python.test/src/tests/test_interop.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -38,14 +38,23 @@
3838
# SOFTWARE.
3939

4040
import os
41-
from unittest import skipIf
41+
import unittest
42+
from unittest import skipIf, skipUnless
4243

4344
import sys
4445

4546
if sys.implementation.name == "graalpy":
4647
import polyglot
4748
from __graalpython__ import is_native
4849

50+
try:
51+
polyglot.eval(language="ruby", string="1")
52+
polyglot.eval(language="js", string="1")
53+
except:
54+
test_polyglot_languages = False
55+
else:
56+
test_polyglot_languages = True
57+
4958
def test_import():
5059
def some_function():
5160
return "hello, polyglot world!"
@@ -640,3 +649,19 @@ def test_jython_star_import():
640649
g = {}
641650
exec('from java.lang.Byte import *', g)
642651
assert type(g['MAX_VALUE']) is int
652+
653+
@skipUnless(test_polyglot_languages, "tests other language access")
654+
def test_doctest():
655+
import doctest
656+
657+
class Example(doctest.Example):
658+
"""
659+
Subclass of doctest.Example that accepts the end of
660+
markdown code blocks as end of examples.
661+
"""
662+
def __init__(self, source, want, *args, **kwargs):
663+
want = want.rstrip("```\n")
664+
super(Example, self).__init__(source, want, *args, **kwargs)
665+
doctest.Example = Example
666+
667+
assert doctest.testmod(m=polyglot, verbose=getattr(unittest, "verbose"), optionflags=doctest.ELLIPSIS).failed == 0

0 commit comments

Comments
 (0)