Skip to content

Commit 8a9ac66

Browse files
gselzerclaude
andcommitted
Declare parameters with the types Jython converts them to
Jython hands scripts Python values for strings, numbers and booleans (e.g. a java.lang.String becomes a unicode, whose Java methods fail), and array.array for arrays. Declaring them so, Jython's analysis no longer offers Java members that fail at run time (e.g. name.toUpperCase), and jedi sees the same types as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 657bb21 commit 8a9ac66

3 files changed

Lines changed: 45 additions & 24 deletions

File tree

‎src/main/java/org/scijava/plugins/scripting/jython/JythonDialect.java‎

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
*/
2929
package org.scijava.plugins.scripting.jython;
3030

31+
import java.math.BigInteger;
3132
import java.util.ArrayList;
3233
import java.util.Collection;
3334
import java.util.List;
@@ -37,13 +38,15 @@
3738
import org.scijava.code.lsp.script.ScriptDialect;
3839

3940
/**
40-
* How Jython scripts see their script parameters: as the Java objects
41-
* themselves (primitives boxed), e.g. {@code net.imglib2.img.Img}.
41+
* How Jython scripts see their script parameters: as the Python values Jython
42+
* converts them to (e.g. {@code str} for strings, {@code int} for integers,
43+
* {@code array.array} for arrays), or else as the Java objects themselves
44+
* (e.g. {@code net.imglib2.img.Img}).
4245
* <p>
43-
* Each is declared as an instance of its class, in code both Jython's own
46+
* Each is declared as an instance of its type, in code both Jython's own
4447
* analysis (Python 2) and a Python language server knowing the Java packages
4548
* from stubs (see {@link JavaStubs}) read: e.g. {@code import net.imglib2.img;
46-
* image = net.imglib2.img.Img()}. (Arrays are lists.)
49+
* image = net.imglib2.img.Img(); name = str()}.
4750
* </p>
4851
*
4952
* @author Gabriel Selzer
@@ -52,13 +55,20 @@ class JythonDialect implements ScriptDialect {
5255

5356
@Override
5457
public String runtimeType(final Class<?> type) {
55-
return boxed(type).getName();
58+
final Class<?> t = boxed(type);
59+
// NB: As Jython converts them (Py.java2py).
60+
if (t == String.class || t == Character.class) return "str";
61+
if (t == Integer.class || t == Long.class || t == Short.class ||
62+
t == Byte.class || t == BigInteger.class) return "int";
63+
if (t == Double.class || t == Float.class) return "float";
64+
if (t == Boolean.class) return "bool";
65+
if (t.isArray()) return "array.array";
66+
return t.getName();
5667
}
5768

5869
@Override
5970
public String declare(final String name, final String type) {
60-
return name + " = " + (type.startsWith("[") ? "list" : type.replace('$',
61-
'.')) + "()";
71+
return name + " = " + type.replace('$', '.') + "()";
6272
}
6373

6474
@Override
@@ -68,17 +78,20 @@ public List<String> imports(final Collection<String> types) {
6878
return imports;
6979
}
7080

71-
/** The Java packages of the given types that a declaration refers to. */
81+
/**
82+
* The packages or modules of the given types that a declaration refers to
83+
* (none for builtins, e.g. {@code str}).
84+
*/
7285
static Set<String> packages(final Collection<String> types) {
7386
final Set<String> packages = new TreeSet<>();
7487
for (final String t : types) {
75-
if (!t.startsWith("[") && t.lastIndexOf('.') > 0) packages.add(t
76-
.substring(0, t.lastIndexOf('.')));
88+
if (t.lastIndexOf('.') > 0) packages.add(t.substring(0, t.lastIndexOf(
89+
'.')));
7790
}
7891
return packages;
7992
}
8093

81-
/** Maps primitive types to their wrappers, whose members can be listed. */
94+
/** Maps primitive types to their wrappers. */
8295
private static Class<?> boxed(final Class<?> type) {
8396
if (!type.isPrimitive()) return type;
8497
if (type == int.class) return Integer.class;

‎src/test/java/org/scijava/plugins/scripting/jython/JythonCompletionTest.java‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,16 @@ public void testScriptParameters() {
140140
"#@ NoSuchType mystery\n";
141141
assertTrue(complete(header + "x = na").contains("name"));
142142
assertTrue(complete(header + "myst").contains("mystery"));
143-
final List<String> nameMembers = complete(header + "name.toUpp");
144-
assertTrue("expected name.toUpperCase among " + nameMembers, nameMembers
145-
.contains("name.toUpperCase"));
143+
// As the Python values Jython converts them to: e.g. strings are str,
144+
// whose members are Python's, not java.lang.String's.
145+
final List<String> nameMembers = complete(header + "name.upp");
146+
assertTrue("expected name.upper among " + nameMembers, nameMembers
147+
.contains("name.upper"));
148+
assertTrue(complete(header + "name.toUpp").isEmpty());
146149
// Outputs are known too, even before the script assigns them.
147-
assertTrue(complete(header + "greeting.len").contains("greeting.length"));
148-
// Primitive parameters complete as their wrapper type.
149-
assertTrue(complete(header + "count.intV").contains("count.intValue"));
150+
assertTrue(complete(header + "greeting.st").contains("greeting.strip"));
151+
// Numbers are int, not java.lang.Integer.
152+
assertFalse(complete(header + "count.intV").contains("count.intValue"));
150153
}
151154

152155
@Test

‎src/test/java/org/scijava/plugins/scripting/jython/JythonServerCompletionTest.java‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,21 @@ public void testDeclarations() {
120120
final JythonDialect dialect = new JythonDialect();
121121
assertEquals("img = net.imglib2.img.Img()", dialect.declare("img",
122122
"net.imglib2.img.Img"));
123-
assertEquals("n = java.lang.Integer()", dialect.declare("n", dialect
124-
.runtimeType(int.class)));
123+
// As Jython converts them: e.g. strings and numbers to Python's.
124+
assertEquals("str", dialect.runtimeType(String.class));
125+
assertEquals("int", dialect.runtimeType(int.class));
126+
assertEquals("int", dialect.runtimeType(Long.class));
127+
assertEquals("float", dialect.runtimeType(double.class));
128+
assertEquals("bool", dialect.runtimeType(boolean.class));
129+
assertEquals("array.array", dialect.runtimeType(double[].class));
130+
assertEquals("java.math.BigDecimal", dialect.runtimeType(
131+
java.math.BigDecimal.class));
132+
assertEquals("n = int()", dialect.declare("n", "int"));
125133
assertEquals("e = java.util.Map.Entry()", dialect.declare("e",
126134
"java.util.Map$Entry"));
127-
assertEquals("a = list()", dialect.declare("a", dialect.runtimeType(
128-
double[].class)));
129-
assertEquals(Arrays.asList("import java.lang", "import java.util",
135+
assertEquals(Arrays.asList("import array", "import java.util",
130136
"import net.imglib2.img"), dialect.imports(Arrays.asList(
131-
"net.imglib2.img.Img", "java.lang.String", "java.util.Map$Entry",
132-
"[D")));
137+
"net.imglib2.img.Img", "str", "java.util.Map$Entry", "array.array")));
133138
assertTrue(JythonServerPlugin.isJython(jython));
134139
}
135140
}

0 commit comments

Comments
 (0)