1

I'm trying to call the PJSUA2 library from Python, it works fine but I hit a snag trying to call

void utilAddPendingJob(PendingJob *job)

which results in the following error

TypeError: in method 'Endpoint_utilAddPendingJob', argument 2 of type 'pj::PendingJob *'

The Python code is as follows:

import pjsua2 as pj

class MyJob(pj.PendingJob):
    def __init__(self, text):
        self.text = text

    def execute(self, is_pending = False):
        print(text)

<<SNIP>>

job = MyJob("test")
pj.Endpoint.instance().utilAddPendingJob(job)

The only difference I see is that this function takes a pointer instead of a reference on the C++ side. However, looking through the SWIG manual this shouldn't matter.

Edit: Here is the PendingJob SWIG generated Python class:

class PendingJob(_object):
    __swig_setmethods__ = {}
    __setattr__ = lambda self, name, value: _swig_setattr(self, PendingJob, name, value)
    __swig_getmethods__ = {}
    __getattr__ = lambda self, name: _swig_getattr(self, PendingJob, name)

    def __init__(self, *args, **kwargs):
        raise AttributeError("No constructor defined - class is abstract")
    __repr__ = _swig_repr

    def execute(self, is_pending):
        return _pjsua2.PendingJob_execute(self, is_pending)
    __swig_destroy__ = _pjsua2.delete_PendingJob
    __del__ = lambda self: None
PendingJob_swigregister = _pjsua2.PendingJob_swigregister
PendingJob_swigregister(PendingJob)

And the method signature, again SWIG generated Python code:

    def utilAddPendingJob(self, job):
        return _pjsua2.Endpoint_utilAddPendingJob(self, job)
Rob
  • 21
  • 3
  • You've shown a class called `AudioJob` but then created an instance of a class called `MyJob`. Given that I think the issue lies in the class you've defined can you clarify what's going on? – Flexo Jun 29 '19 at 17:03
  • You're completely right, it was a typo. Updated it now, thanks! – Rob Jun 29 '19 at 20:48

1 Answers1

0

It looks like there's one simple mistake here that's preventing SWIG from realising that the object you're creating is actually an instance of pj.PendingJob - you've not called the super class's __init__ function.

Add that in like so:

class MyJob(pj.PendingJob):
    def __init__(self, text):
        self.text = text
        super().__init__() # Python3 niceness, alternatives available for Python2

and I think you'll be back in business.

(The super class constructor is important in SWIG always, it's what creates a real C++ instance that implements your virtual functions).

In this case however it seems that the wrapper you're using hasn't been set up to allow cross-language polymorphism on the pj.PendingJob class. If you checkout pjsip and then run this:

find . -iname '*.i' -execdir cat {} \; | grep 'director'

Then there's no reference to PendingJob anywhere to be seen (and no macros hiding it either).

For the sake of understanding let's validate this with an experiment:

%module(directors="1") test

%director Test2;

%inline %{
struct Test1 {
  virtual ~Test1() {}
  virtual void blah() = 0;
};
%}

%inline %{
struct Test2 {
  virtual ~Test2() {}
  virtual void blah() = 0;
};
%}

When we run this through SWIG we see the __init__ generated for the two classes differs. For the class which has a %director directive (i.e. Test2) it's callable and does a bunch of work to setup ready for cross language polymorphism. For Test1 we see the same __init__ as you reported in the constructor, i.e. it just raises an exception because the type is abstract. (For reference it's not totally useless still because you can receive concrete instance of that type from C++, it's just that you can't create or extend them in Python).

Flexo
  • 87,323
  • 22
  • 191
  • 272
  • Unfortunately that doesn't work. The object is abstract so calling super will throw an exception. – Rob Jun 30 '19 at 18:10
  • Well you can't create an instance of it if it's abstract. You'll have to implement the pure virtual methods. There's an example of exactly this in the SWIG manual: http://swig.org/Doc3.0/Python.html#Python_nn33 – Flexo Jun 30 '19 at 21:33
  • I think I already did all that. I updated the question with the relevant SWIG generated Python code – Rob Jul 01 '19 at 15:02
  • Looks pretty likely that class doesn't have directors turned on for it then which would mean no cross language polymorphism. I'll investigate more and confirm properly in a bit. – Flexo Jul 01 '19 at 16:38
  • @Rob looks like it's impossible in this case because they've not enabled cross language polymorphism for this class. I think they probably should have from a quick scan of the source, it'd be worth a patch to pjsip-apps/src/swig/pjsua2.i which adds `%feature("director") PendingJob`. (You could actually do something quite neat with callables in Python and runnables in Java). – Flexo Jul 02 '19 at 07:26