Skip to content

Commit 2713b6c

Browse files
committed
Make jreflect function more powerful
1 parent a31701b commit 2713b6c

File tree

3 files changed

+49
-30
lines changed

3 files changed

+49
-30
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ AttributeError: 'list' object has no attribute 'stream'
135135
Traceback (most recent call last):
136136
File "<stdin>", line 1, in <module>
137137
TypeError: No matching overloads found for java.util.Set.addAll(set), options are:
138-
public abstract boolean java.util.Set.addAll(java.util.Collection)
138+
public abstract boolean java.util.Set.addAll(java.util.Collection)
139139
>>> from scyjava import to_java as p2j
140140
>>> jset.addAll(p2j(pset))
141141
True
@@ -325,13 +325,13 @@ FUNCTIONS
325325
:param jtype: The Java type, as either a jimported class or as a string.
326326
:return: True iff the object is an instance of that Java type.
327327

328-
jreflect(data, aspect: str) -> List[Dict[str, Any]]
328+
jreflect(data, aspect: str = "all") -> List[Dict[str, Any]]
329329
Use Java reflection to introspect the given Java object,
330330
returning a table of its available methods or fields.
331331

332332
:param data: The object or class or fully qualified class name to inspect.
333-
:param aspect: Either "methods" or "fields"
334-
:return: List of dicts with keys: "name", "static", "arguments", and "returns".
333+
:param aspect: One of: "all", "constructors", "fields", or "methods".
334+
:return: List of dicts with keys: "name", "mods", "arguments", and "returns".
335335

336336
jstacktrace(exc) -> str
337337
Extract the Java-side stack trace from a Java exception.

src/scyjava/_introspect.py

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,54 +10,73 @@ class methods, fields, and source code URL.
1010
from scyjava._types import isjava, jinstance, jclass
1111

1212

13-
def jreflect(data, aspect: str) -> List[Dict[str, Any]]:
13+
def jreflect(data, aspect: str = "all") -> List[Dict[str, Any]]:
1414
"""
1515
Use Java reflection to introspect the given Java object,
1616
returning a table of its available methods or fields.
1717
1818
:param data: The object or class or fully qualified class name to inspect.
19-
:param aspect: Either "methods" or "fields"
20-
:return: List of dicts with keys: "name", "static", "arguments", and "returns".
19+
:param aspect: One of: "all", "constructors", "fields", or "methods".
20+
:return: List of dicts with keys: "name", "mods", "arguments", and "returns".
2121
"""
2222

23+
aspects = ["all", "constructors", "fields", "methods"]
24+
if aspect not in aspects:
25+
raise ValueError("aspect must be one of {aspects}")
26+
2327
if not isjava(data) and isinstance(data, str):
2428
try:
2529
data = jimport(data)
26-
except Exception as err:
27-
raise ValueError(f"Not a Java object {err}")
30+
except Exception as e:
31+
raise ValueError(
32+
f"Object of type '{type(data).__name__}' is not a Java object"
33+
) from e
2834

29-
Modifier = jimport("java.lang.reflect.Modifier")
3035
jcls = data if jinstance(data, "java.lang.Class") else jclass(data)
3136

32-
if aspect == "methods":
33-
cls_aspects = jcls.getMethods()
34-
elif aspect == "fields":
35-
cls_aspects = jcls.getFields()
36-
else:
37-
return '`aspect` must be either "fields" or "methods"'
37+
Modifier = jimport("java.lang.reflect.Modifier")
38+
modifiers = {
39+
attr[2:].lower(): getattr(Modifier, attr)
40+
for attr in dir(Modifier)
41+
if attr.startswith("is")
42+
}
43+
44+
members = []
45+
if aspect in ["all", "constructors"]:
46+
members.extend(jcls.getConstructors())
47+
if aspect in ["all", "fields"]:
48+
members.extend(jcls.getFields())
49+
if aspect in ["all", "methods"]:
50+
members.extend(jcls.getMethods())
3851

3952
table = []
4053

41-
for m in cls_aspects:
42-
name = m.getName()
43-
if aspect == "methods":
44-
args = [c.getName() for c in m.getParameterTypes()]
45-
returns = m.getReturnType().getName()
46-
elif aspect == "fields":
47-
args = None
48-
returns = m.getType().getName()
49-
mods = Modifier.isStatic(m.getModifiers())
54+
for member in members:
55+
mtype = str(member.getClass().getName()).split(".")[-1].lower()
56+
name = member.getName()
57+
modflags = member.getModifiers()
58+
mods = [name for name, hasmod in modifiers.items() if hasmod(modflags)]
59+
args = (
60+
[ptype.getName() for ptype in member.getParameterTypes()]
61+
if hasattr(member, "getParameterTypes")
62+
else None
63+
)
64+
returns = (
65+
member.getReturnType().getName()
66+
if hasattr(member, "getReturnType")
67+
else (member.getType().getName() if hasattr(member, "getType") else None)
68+
)
5069
table.append(
5170
{
71+
"type": mtype,
5272
"name": name,
53-
"static": mods,
73+
"mods": mods,
5474
"arguments": args,
5575
"returns": returns,
5676
}
5777
)
58-
sorted_table = sorted(table, key=lambda d: d["name"])
5978

60-
return sorted_table
79+
return table
6180

6281

6382
def _map_syntax(base_type):
@@ -98,7 +117,7 @@ def _make_pretty_string(entry, offset):
98117
return_val = f"{entry['returns'].__str__():<{offset}}"
99118
# Handle whether to print static/instance modifiers
100119
obj_name = f"{entry['name']}"
101-
modifier = f"{'*':>4}" if entry["static"] else f"{'':>4}"
120+
modifier = f"{'*':>4}" if "static" in entry["mods"] else f"{'':>4}"
102121

103122
# Handle fields
104123
if entry["arguments"] is None:

tests/test_introspect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,6 @@ def test_imagej_legacy(self):
6363
return
6464
str_RE = "ij.plugin.RoiEnlarger"
6565
table = scyjava.jreflect(str_RE, aspect="methods")
66-
assert len([entry for entry in table if entry["static"]]) == 3
66+
assert sum(1 for entry in table if "static" in entry["mods"]) == 3
6767
repo_path = "https://github.com/imagej/ImageJ/"
6868
assert scyjava.jsource(str_RE).startsWith(repo_path)

0 commit comments

Comments
 (0)