Kotlin / kotlin-jupyter

Kotlin kernel for Jupyter/IPython
Apache License 2.0
1.12k stars 105 forks source link

class loading behaviour #403

Closed jbaron closed 1 year ago

jbaron commented 1 year ago

There is a difference in class loading behaviour using @file:DependsOn("...") or using dependencies(....) in the Integration class.

When using the dependencies(....) approach, the loaded classes cannot access other classes loaded using @file:DependsOn. Seems like @file:DependsOn and dependencies(....) use different class loaders?

ileasile commented 1 year ago

What exactly do you mean? What series of snippets do you compare? I don't need exact libraries, just the way you're using @file:DependsOn and dependencies

jbaron commented 1 year ago

The use case is:

  1. I include a file in my notebook using: @file:DependsOn("locallib.jar")

  2. I run "%use roboquant" and in the JupyterIntegration subclass I call dependencies("x:y:z"). This "x:y:z" has a dependency on "locallib.jar". However this doesn't get resolved.

I guess difficult to pinpoint with this description. I have a workarounds, so not urgent. And if I have some time, will try to investigate a bit more.

ileasile commented 1 year ago

I see. Actually calling dependencies("x:y:z") will generate code @file:DependsOn("x:y:z") and will execute it. So it's mostly the same. Classloaders are obviously different, but older classloader should be a parent to the latter one. It is not a native library, right? And another thing is what classloader is used for loading classes. In some libraries, system classloader is used explicitly which is wrong. The right one could be taken from Thread.currentClassLoader() or KotlinKernelHost.lastClassLoader

jbaron commented 1 year ago

Ahh, classloaders inherit based on their "age", explains the behaviour I noticed.

If I put the following two statements in a single notebook input cell, it causes the issue of the second statement not being able to resolve a dependency on the first one:

@file:DependsOn("TwsApi.jar")
%use roboquant(modules=ibkr) // triggers a call to dependencies("org.roboquant:roboquant-ibkr:1.2.0")

But if I put the same two statements in their own separate input cells (and thus forcing the first one to become the parent class loader of the second one), it works fine.

Seems a workable solution for me, so I will close this issue if ok.

ileasile commented 1 year ago

Yes, you're right. And one unobvious thing here: %use roboquant(modules=ibkr) will be executed before @file:DependsOn("TwsApi.jar"), because magics are always processed first, regardless of the real order of the lines