ninia / jep

Embed Python in Java
Other
1.3k stars 147 forks source link

Something like .hasAttr in PyObject? #417

Open Daniel-Alievsky opened 2 years ago

Daniel-Alievsky commented 2 years ago

In my project I need to detect, whether some Python object, created by my user in his Python script, has some attribute (usually simple field) or no. If it has, it will become one of results of his function inside our system, if no, the corresponding result will stay empty.

Unfortunately, I didn't find, in PyObject class, anything like ".hasAttr". I found following solutions.

1) I can call standard function hasattr(). But I cannot call it from Java without explicit import from "builtins". I do not control Python code of the user and do not want to require executing special non-obvious operation like ""from builtins import hasattr" at the very beginning of any JEP usage. Due to the same reasons I cannot use built-in dir() function.

2) I can try to call "dir()" method. But, first, it is inconvenient: after this I'll need to search inside ArrayList. Second, there is no guarantees that Python object has such a method, and we return to the original issue: how to detect that such method exists.

3) I can catch an exception. It is the solution that I've chosen in my AtomicPyObject:

    public Object getAttributeOrNull(String name) throws JepException {
        return i.executeInSingleThread(() -> {
            try {
                return pyObject.getAttr(name);
            } catch (JepException e) {
                return null;
            }
        });
    }

But ignoring exceptions is considered to be a bad practice in Java. Maybe, JepException will be caused by some other reasons, like invalid thread?

So, could you implement something like standard '.hasAttr' method inside your PyObject? I believe you can find the best way for checking existence of an attribute, and it would become an obvious standard method to do this.

ndjensen commented 2 years ago

I don't think we want to add hasAttr to Jep's Java PyObject since the Python data model does not have a __hasattr__ method. https://docs.python.org/3/reference/datamodel.html#customizing-attribute-access

Daniel-Alievsky commented 2 years ago

I don't think we want to add hasAttr to Jep's Java PyObject since the Python data model does not have a hasattr method. https://docs.python.org/3/reference/datamodel.html#customizing-attribute-access

I well understand your reasons. But if it was not a problem for Python, where we has a standard and always available function "hasattr()", if became a problem in Java for JEP users, as I described. It requires some workaround, and I already offered 3 variants, but all they are not obvious and look terribly. It would be much better if you will implement corresponding workaround inside JEP library, invisible for your users.

At least, you could catch an exception from native method getAttr(long tstate, long pyObject, String attr_name, Class<?> clazz) It is much better than catching on user's side, like in my code, because the user cannot distinguish between JepException due to absence of attribute and JepException thrown in checkValid().

But, probably, on C++ level you may find even better and faster solution, without catching Java exceptions (relatively slow operation in Java).