Skip to content

Commit 006e56c

Browse files
committed
Added guide to implement Traverseproc with code samples
to doc of Traverseproc.java.
1 parent a21e9ff commit 006e56c

File tree

4 files changed

+226
-52
lines changed

4 files changed

+226
-52
lines changed

src/org/python/core/BaseSet.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -606,16 +606,18 @@ public Object[] toArray(Object a[]) {
606606
public int traverse(Visitproc visit, Object arg) {
607607
int retValue;
608608
for (PyObject ob: _set) {
609-
retValue = visit.visit(ob, arg);
610-
if (retValue != 0) {
611-
return retValue;
609+
if (ob != null) {
610+
retValue = visit.visit(ob, arg);
611+
if (retValue != 0) {
612+
return retValue;
613+
}
612614
}
613615
}
614616
return 0;
615617
}
616618

617619
@Override
618620
public boolean refersDirectlyTo(PyObject ob) {
619-
return _set.contains(ob);
621+
return ob != null && _set.contains(ob);
620622
}
621623
}

src/org/python/core/PySystemState.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,27 +1660,6 @@ public void run() {
16601660
/* Traverseproc implementation */
16611661
@Override
16621662
public int traverse(Visitproc visit, Object arg) {
1663-
// Potential PyObject refs in PySystemState:
1664-
// public PyList argv = new PyList();
1665-
// public PyObject modules;
1666-
// public PyList path;
1667-
// public PyList warnoptions = new PyList();
1668-
// public PyObject builtins;
1669-
// public PyObject platform = defaultPlatform;
1670-
// public PyList meta_path;
1671-
// public PyList path_hooks;
1672-
// public PyObject path_importer_cache;
1673-
// public PyObject ps1 = new PyString(">>> ");
1674-
// public PyObject ps2 = new PyString("... ");
1675-
// public PyObject executable;
1676-
// public PyObject stdout, stderr, stdin;
1677-
// public PyObject __stdout__, __stderr__, __stdin__;
1678-
// public PyObject __displayhook__, __excepthook__;
1679-
// public PyObject last_value = Py.None;
1680-
// public PyObject last_type = Py.None;
1681-
// public PyObject last_traceback = Py.None;
1682-
// public PyObject __name__ = new PyString("sys");
1683-
// public PyObject __dict__;
16841663
int retVal;
16851664
if (argv != null) {
16861665
retVal = visit.visit(argv, arg);

src/org/python/core/Traverseproc.java

Lines changed: 216 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package org.python.core;
22

3+
import org.python.expose.ExposedType;
4+
import org.python.modules.gc;
5+
36
/**
47
* <p>
58
* This interface defines a
69
* <a href="https://docs.python.org/2.7/c-api/gcsupport.html" target="_blank">
7-
* CPython equivalent traverse mechanism
10+
* CPython-equivalent traverse-mechanism
811
* </a> allowing to detect reference cycles. While this is crucial for cyclic
9-
* gc support in CPython, it only serves debugging purposes in Jython. As a side
10-
* effect it allows a more complete implementation of the gc module.
12+
* gc support in CPython, it only serves debugging purposes in Jython. As a
13+
* side-effect it allows a more complete implementation of the gc module.
1114
* </p>
1215
* <p>
1316
* Note that implementing this interface is only OPTIONAL.<b> Gc will work fine
@@ -16,13 +19,13 @@
1619
* custom PyObject-implementations.
1720
* </p>
1821
* <p>
19-
* Of course this interface shall only be implemented by PyObjects that
20-
* potentially own direct references to other PyObjects. Note that indirect
22+
* Of course this interface shall only be implemented by {@code PyObject}s that
23+
* potentially own direct references to other {@code PyObject}s. Note that indirect
2124
* references via non-PyObjects should also be treated as "direct" (c.f.
2225
* tracefunc in {@link org.python.core.PyFrame}).
23-
* PyObjects that don't own references to other PyObjects under any condition
24-
* and neither inherit such references from a superclass are strictly recommended
25-
* to be annotated {@link org.python.core.Untraversable}.
26+
* {@code PyObject}s that don't own references to other {@code PyObject}s under any
27+
* condition and neither inherit such references from a superclass are strictly
28+
* recommended to be annotated {@link org.python.core.Untraversable}.
2629
* </p>
2730
* <p>
2831
* Jython's traverse mechanism serves debugging purposes to ease finding memory
@@ -31,19 +34,19 @@
3134
* from these different behaviors. Jython's traverse mechanism is intended to
3235
* allow finding such bugs by comparing gc behavior to CPython and isolating
3336
* the Python code that is not robust enough to work invariant under different
34-
* gc behaviors.
37+
* gc behaviors. See also {@link org.python.modules.gc} for more details on this.
3538
* </p>
3639
* <p>
3740
* Further this mechanism is crucial for some aspects of gc-support of the
3841
* <a href="http://www.jyni.org" target="_blank">JyNI</a>
3942
* project. JyNI does not strictly depend on it to emulate CPython's gc
4043
* for extensions, but would have to perform inefficient reflection-based
41-
* traversal in some edge-cases (which might also conflict security managers).
44+
* traversal in some edge-cases (which might also conflict with security managers).
4245
* </p>
4346
* <p>
44-
* Note that the slots-array and - if existent - the user-dict of fooDerived classes
45-
* is traversed by {@link org.python.core.TraverseProcDerived}.
46-
* The gc module takes care of exploiting both traverse methods in its static traverse
47+
* Note that the slots-array and - if existent - the user-dict of {@code fooDerived}
48+
* classes is traversed by {@link org.python.core.TraverseProcDerived}.
49+
* The gc-module takes care of exploiting both traverse methods in its static traverse
4750
* method. So for manual traversion one should always use
4851
* {@link org.python.modules.gc#traverse(PyObject, Visitproc, Object)} rather
4952
* than directly calling methods in this interface.
@@ -57,25 +60,209 @@
5760
* traversed (along with the user dict).
5861
* </p>
5962
* <p>
60-
* Note for implementing:<br>
61-
* Every non-static strong referenced PyObject should be passed to the
63+
* <b>Note for implementing:</b><br>
64+
* Every non-static, strong-referenced {@code PyObject} should be passed to the
6265
* {@link org.python.core.Visitproc}. If {@code Object}s or {@code interface}-types are
6366
* referenced where it is not known, whether it is a {@code PyObject} or
6467
* references other {@code PyObjects}, one should check for {@code PyObject}
65-
* via {@code instanceof} and otherwise also check whether it at least
66-
* implements {@code Traverseproc} itself. In latter case one should traverse
67-
* it by delegating to its {@code Traverseproc} methods.<br>
68-
* Warning:<br>
68+
* via {@code instanceof}. If a non-{@code PyObject}
69+
* implements {@code Traverseproc}, one can traverse
70+
* it by delegating to its {@code Traverseproc} methods.<br><br>
71+
* <b>Warning:</b><br>
6972
* If one lets non-{@code PyObject}s implement {@code Traverseproc}, extreme
7073
* care must be taken, whether the traverse call shall be passed on to other
7174
* non-{@code PyObject} {@code Traverseproc}-implementers, as this can cause
7275
* infinite traverse cycles.<br>
7376
* Examples for non-{@code PyObject}s that implement {@code Traverseproc} are
7477
* {@link org.python.core.PyException} and {@link com.ziclix.python.sql.Fetch}.
78+
* A safer, but potentially slower way to deal with
79+
* non-{@code PyObject}-{@code Traverseproc}s or any other non-{@code PyObject}
80+
* that might contain references to other {@code PyObject}s is
81+
* {@link org.python.modules.gc#traverseByReflection(Object, Visitproc, Object)}.
82+
* This is for instance used in {@link org.python.core.PyArray}.
83+
* </p>
84+
* <p>
85+
* <br>
86+
* <b>Examples</b><br><br>
87+
* In the following we provide some examples with code-snippets to demonstrate
88+
* and streamline the writing of {@code Traverseproc}-implementations.<br>
89+
* Since this peace of API was introduced to enhance a large existing
90+
* code-base, we recommend to put the {@code Traverseproc}-implementation always
91+
* to the end of a class and separate it from the original code by two blank
92+
* lines and a comment "Traverseproc implementation".<br><br>
93+
* Let's start with classes that don't hold references to {@code PyObject}s.
94+
* If the class extends some other class that implements {@code Traverseproc}, nothing
95+
* special needs to be done. For instance, we have this situation in
96+
* {@link org.python.core.PySet}. It extends {@link org.python.core.BaseSet},
97+
* which in turn implements {@code Traverseproc}:<br><br>
98+
* <pre>
99+
* {@literal @}ExposedType(name = "set", base = PyObject.class, doc = BuiltinDocs.set_doc)
100+
* public class PySet extends BaseSet {
101+
* ...
102+
* }
103+
* </pre>
104+
* If the class neither contains {@code PyObject}-references, nor extends some
105+
* {@code Traverseproc}-implementing class, it is recommended to be annotated
106+
* {@link org.python.core.Untraversable}. {@link org.python.core.PyInteger} is
107+
* an example for this:<br><br>
108+
* <pre>
109+
* {@literal @}Untraversable
110+
* {@literal @}ExposedType(name = "int", doc = BuiltinDocs.int_doc)
111+
* public class PyInteger extends PyObject {
112+
* ...
113+
* }
114+
* </pre>
115+
* If there are simply some {@code PyObject}(-subclass), non-static fields in the class,
116+
* let it implement {@link org.python.core.Traverseproc}.
117+
* Write {@link org.python.core.Traverseproc#traverse(Visitproc, Object)} by
118+
* just visiting the fields one by one. Check each to be non-{@code null} previously
119+
* unless the field cannot be {@code null} for some good reason. If
120+
* {@link org.python.core.Visitproc#visit(PyObject, Object)} returns non-zero,
121+
* return the result immediately (i.e. abort the traverse process).
122+
* The following example is taken from
123+
* {@link org.python.core.PyMethod}:<br><br>
124+
* <pre>
125+
* /{@literal *} Traverseproc implementation {@literal *}/
126+
* {@literal @}Override
127+
* public int traverse(Visitproc visit, Object arg) {
128+
* int retVal;
129+
* if (im_class != null) {
130+
* retVal = visit.visit(im_class, arg);
131+
* if (retVal != 0) {
132+
* return retVal;
133+
* }
134+
* }
135+
* if (__func__ != null) {
136+
* retVal = visit.visit(__func__, arg);
137+
* if (retVal != 0) {
138+
* return retVal;
139+
* }
140+
* }
141+
* return __self__ == null ? 0 : visit.visit(__self__, arg);
142+
* }
143+
* </pre>
144+
* Implement {@link org.python.core.Traverseproc#refersDirectlyTo(PyObject)}
145+
* by checking the argument to be non-{@code null} and identity-comparing it to
146+
* every field:<br><br>
147+
* <pre>
148+
* {@literal @}Override
149+
* public boolean refersDirectlyTo(PyObject ob) {
150+
* return ob != null && (ob == im_class || ob == __func__ || ob == __self__);
151+
* }
152+
* </pre>
153+
* If there is a Java-set or other iterable that it is not a {@code PyObject},
154+
* but contains {@code PyObject}s, visit every element. Don't forget to check
155+
* for non-{@code null} if necessary and return immediately, if
156+
* {@link org.python.core.Visitproc#visit(PyObject, Object)} returns non-zero.
157+
* The following example is taken from {@link org.python.core.BaseSet}:<br><br>
158+
* <pre>
159+
* /{@literal *} Traverseproc implementation {@literal *}/
160+
* {@literal @}Override
161+
* public int traverse(Visitproc visit, Object arg) {
162+
* int retValue;
163+
* for (PyObject ob: _set) {
164+
* if (ob != null) {
165+
* retValue = visit.visit(ob, arg);
166+
* if (retValue != 0) {
167+
* return retValue;
168+
* }
169+
* }
170+
* }
171+
* return 0;
172+
* }
173+
* </pre>
174+
* In this case, {@link org.python.core.Traverseproc#refersDirectlyTo(PyObject)}
175+
* can be implemented (potentially) efficiently by using the backing set's
176+
* {@code contains}-method:<br><br>
177+
* <pre>
178+
* {@literal @}Override
179+
* public boolean refersDirectlyTo(PyObject ob) {
180+
* return ob != null && _set.contains(ob);
181+
* }
182+
* </pre>
183+
* If a class extends a {@code Traverseproc}-implementing class and adds
184+
* {@code PyObject}-references to it, the parent-{@code traverse}-method
185+
* should be called initially via {@code super} (example is taken from
186+
* {@link org.python.core.PyJavaType}):<br><br>
187+
* <pre>
188+
* /{@literal *} Traverseproc implementation {@literal *}/
189+
* {@literal @}Override
190+
* public int traverse(Visitproc visit, Object arg) {
191+
* int retVal = super.traverse(visit, arg);
192+
* if (retVal != 0) {
193+
* return retVal;
194+
* }
195+
* if (conflicted != null) {
196+
* for (PyObject ob: conflicted) {
197+
* if (ob != null) {
198+
* retVal = visit.visit(ob, arg);
199+
* if (retVal != 0) {
200+
* return retVal;
201+
* }
202+
* }
203+
* }
204+
* }
205+
* return 0;
206+
* }
207+
* </pre>
208+
* In contrast to that, {@link org.python.core.Traverseproc#refersDirectlyTo(PyObject)}
209+
* should call its parent-method as late as possible, because that method might throw an
210+
* {@code UnsupportedOperationException}. By calling it in the end, we have the chance
211+
* to fail- or succeed fast before a potential exception occurs:<br><br>
212+
* <pre>
213+
* {@literal @}Override
214+
* public boolean refersDirectlyTo(PyObject ob) throws UnsupportedOperationException {
215+
* if (ob == null) {
216+
* return false;
217+
* }
218+
* if (conflicted != null) {
219+
* for (PyObject obj: conflicted) {
220+
* if (obj == ob) {
221+
* return true;
222+
* }
223+
* }
224+
* }
225+
* return super.refersDirectlyTo(ob);
226+
* }
227+
* </pre>
228+
* While reflection-based traversal should be avoided if possible, it can be used to
229+
* traverse fields that might contain references to {@code PyObject}s, but cannot be
230+
* inferred at compile-time.
231+
* {@link org.python.modules.gc#canLinkToPyObject(Class, boolean)} can help to safe
232+
* some performance by failing fast if type-info already rules out the possibility
233+
* of the field holding {@code PyObject}-references.
234+
* This technique is for instance used to traverse the content of
235+
* {@link org.python.core.PyArray}:<br><br>
236+
* <pre>
237+
* /{@literal *} Traverseproc implementation {@literal *}/
238+
* {@literal @}Override
239+
* public int traverse(Visitproc visit, Object arg) {
240+
* if (data == null || !gc.canLinkToPyObject(data.getClass(), true)) {
241+
* return 0;
242+
* }
243+
* return gc.traverseByReflection(data, visit, arg);
244+
* }
245+
* </pre>
246+
* {@link org.python.modules.gc#canLinkToPyObject(Class, boolean)} also
247+
* offers a way to let {@link org.python.core.Traverseproc#refersDirectlyTo(PyObject)}
248+
* fail fast by type-information:<br><br>
249+
* <pre>
250+
* {@literal @}Override
251+
* public boolean refersDirectlyTo(PyObject ob)
252+
* throws UnsupportedOperationException {
253+
* if (data == null || !gc.canLinkToPyObject(data.getClass(), true)) {
254+
* return false;
255+
* }
256+
* throw new UnsupportedOperationException();
257+
* }
258+
* </pre>
75259
* </p>
76260
* <p>
77-
* It follows a list of PyObject subclasses in Jython, excluding derived classes.<br>
78-
* PyObject subclasses in Jython checked for need of Traverseproc:<br>
261+
* <br>
262+
* <b>List of {@code PyObject}-subclasses</b><br><br>
263+
* We conclude with a list of {@code PyObject} subclasses in Jython, excluding
264+
* derived classes.<br>
265+
* {@code PyObject}-subclasses in Jython checked for need of {@code Traverseproc}:<br>
79266
* <br>
80267
* <br>
81268
* org.python.core:<br>
@@ -96,7 +283,6 @@
96283
* BinFunction - no refs, untraversable<br>
97284
* AstList - Traverseproc<br>
98285
* BaseBytes - no refs, untraversable<br>
99-
* IndexDelegate - no PyObject<br>
100286
* BaseDictionaryView - Traverseproc<br>
101287
* BaseSet - Traverseproc<br>
102288
* ClasspathPyImporter - no refs, untraversable<br>
@@ -269,7 +455,9 @@
269455
* PyPartial - Traverseproc<br>
270456
* <br>
271457
* org.python.modules._io:<br>
272-
* PyFileIO - no refs, untraversable (there is a final PyString "mode", which is guarenteed to be a PyString and no subclass; as such it needs not be traversed since it cannot have refs itself)<br>
458+
* PyFileIO - no refs, untraversable (there is a final PyString
459+
* "mode" that is guaranteed to be a PyString and no subclass; as such it needs not be
460+
* traversed since it cannot have refs itself)<br>
273461
* PyIOBase - Traverseproc<br>
274462
* PyRawIOBase - no refs, extends PyIOBase<br>
275463
* <br>
@@ -358,6 +546,9 @@
358546
* Time:<br>
359547
* TimeFunctions - no refs, untraversable<br>
360548
* <br>
549+
* org.python.modules.zipimport:<br>
550+
* zipimporter - Traverseproc<br>
551+
* <br>
361552
* org.python.util:<br>
362553
* InteractiveInterpreter - no PyObject<br>
363554
* <br>
@@ -446,6 +637,8 @@
446637
* @see org.python.core.Untraversable
447638
* @see org.python.core.Visitproc
448639
* @see org.python.modules.gc#traverse(PyObject, Visitproc, Object)
640+
* @see org.python.modules.gc#traverseByReflection(Object, Visitproc, Object)
641+
* @see org.python.modules.gc#canLinkToPyObject(Class, boolean)
449642
*/
450643
public interface Traverseproc {
451644

src/org/python/modules/gc.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import org.python.modules._weakref.GlobalRef;
2828

2929
//These imports belong to the out-commented section on MXBean-based
30-
//GC-sync far below. That section is kept to document this failed
30+
//gc-sync far below. That section is kept to document this failed
3131
//approach and allow easy reproduction of this failure.
3232
//import java.lang.management.*;
3333
//import javax.management.*;
@@ -97,8 +97,8 @@
9797
* Waiting for trash could in theory be strictly synchronized by using {@code MXBean}s, i.e.
9898
* <a href="https://docs.oracle.com/javase/7/docs/jre/api/management/extension/index.html?com/sun/management/GcInfo.html"
9999
* target="_blank">GarbageCollectionNotificationInfo</a> and related API.
100-
* However, experiments showed that the arising GC-notifications do not reliably indicate
101-
* when enqueuing was done for a specific GC-run. We kept the experimental implementation
100+
* However, experiments showed that the arising gc-notifications do not reliably indicate
101+
* when enqueuing was done for a specific gc-run. We kept the experimental implementation
102102
* in source-code comments to allow easy reproducibility of this issue. (Note that out-commented
103103
* code contradicts Jython-styleguide, but this one - however - is needed to document this
104104
* infeasible approach and is explicitly declared accordingly).<br>
@@ -2935,7 +2935,7 @@ private static int traverseByReflectionIntern(Object ob,
29352935
* This method checks via type-checking-only, whether an object
29362936
* of the given class can in principle hold a ref to a {@code PyObject}.
29372937
* Especially if arrays are involved, this can safe a lot performance.
2938-
* For now, no generic-type info is exploited.
2938+
* For now, no generic type-info is exploited.
29392939
* </p>
29402940
* <p>
29412941
* If {@code actual} is true, the answer will hold for an object

0 commit comments

Comments
 (0)