Search code examples
pythonscipydecoratorwrapperode

Why can't I pass a decorated function to scipy.integrate.ode?


When I pass a decorated function to scipy.integrate.ode, the wrapper function gets called, but *args is empty. Why is this happening?

This works:

y0, t0 = 1, 0

def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

This doesn't:

y0, t0 = 1, 0

def dec_func(func):
    def wrap_func(*args, **kwargs):
        return func(*args, **kwargs)
    return wrap_func

@dec_func
def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

It return this error: TypeError: dydt() missing 2 required positional arguments: 't' and 'y'

When I insert a line into the wrapper function to print the length of args, it comes back as 0.

EDIT: This doesn't work either. Same result as main post

from functools import wraps

y0, t0 = 1, 0

def dec_func(func):
    @wraps(func)
    def wrap_func(*args, **kwargs):
        return func(*args, **kwargs)
    return wrap_func

@dec_func
def dydt(t, y):
    return y*0.5

r = scipy.integrate.ode(dydt)
r.set_initial_value(y0)
r.integrate(10)
assert r.successful()

Solution

  • So it's still an open question exactly what is going on inside scipy. Here is the workaround I'm going with.

    y0, t0 = 1, 0
    
    def dec_func(func):
        def wrap_func(t, y, *args, **kwargs):
            return func(t, y, *args, **kwargs)
        return wrap_func
    
    @dec_func
    def dydt(t, y):
        return y*0.5
    
    r = scipy.integrate.ode(dydt)
    r.set_initial_value(y0)
    r.integrate(10)
    assert r.successful()