pythonnet icon indicating copy to clipboard operation
pythonnet copied to clipboard

ScanAssembly does not work as expected

Open BuerkiJ opened this issue 3 years ago • 3 comments

Environment

  • Pythonnet version: 3.0.0rc4
  • Python version: 3.9.7
  • Operating System: Windows 10
  • .NET Runtime: .Net Framework 4.7

Details

  • I want to load an assembly which has references to embbeded dll's with pythonnet into python.
  • When i use clr.load("myAssembly") then pythonnet does not resolve the Namespaces properly.
  • So i digged a bit into the pythonnet source-code. And found that, the call of GetTypes(assembly)) in the Method internal static void ScanAssembly(Assembly assembly) fails with a ReflectionTypeLoadException.
  • https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.gettypes?view=netframework-4.7 states that the exception contains a Types property which contains the found types.
  • So i would expect a try-catch clause here around the ScanAssembly-code and within the catch there should be an adding-mechanism of the found types (the ones which are directly within the loaded assembly and not in the referenced embbeded assemblies).
  • So in my opinion the code should look somehow like this:
internal static void ScanAssembly(Assembly assembly)
{
    if (assembly.GetCustomAttribute<PyExportAttribute>()?.Export == false)
    {
        return;
    }
    try
    {
        foreach (Type t in assembly.GetTypes())
        {
            Same code here as is right now.
        }
    }
    catch (ReflectionTypeLoadException e)
    {
        SomeSuperButNotYetImplementedAddingMethod(e.Types);
        throw;
    }
}

or like this:

internal static void ScanAssembly(Assembly assembly)
{
    if (assembly.GetCustomAttribute<PyExportAttribute>()?.Export == false)
    {
        return;
    }

    Type[] types = new Type[0];
    try
    {
        types = assembly.GetTypes();
    }
    catch (ReflectionTypeLoadException e)
    {
        types = e.Types;
    }

    foreach (Type t in types)
    {
        Same code here as is right now.
    }
}

BuerkiJ avatar Aug 09 '22 10:08 BuerkiJ

I'm not sure I agree. We currently load an assembly either completely or not at all. With the changes you suggest, we would silently load assemblies partially whenever its dependent assemblies can not be loaded, with no way of detecting the situation or "reloading" (which would be even more API).

filmor avatar Aug 10 '22 05:08 filmor

I get your point that it is not really a good idea to load something partially and silently :) But it still gets me the problem right now, that the types defined directly in my assembly are not loaded at all at the moment. Which forces me to use Reflection direct in python. Which also is not really nice ;-)

In the method:

internal static Type[] GetTypes(Assembly a)
{
    if (a.IsDynamic)
    {
        try
        {
            return a.GetTypes().Where(IsExported).ToArray();
        }
        catch (ReflectionTypeLoadException exc)
        {
            // Return all types that were successfully loaded
            return exc.Types.Where(x => x != null && IsExported(x)).ToArray();
        }
    }
    else
    {
        try
        {
            return a.GetExportedTypes().Where(IsExported).ToArray();
        }
        catch (FileNotFoundException)
        {
            return new Type[0];
        }
    }
}

You make a difference between dynamic and not. Wy this? In Case of dynamic you also just load the types partially and silently. If you would not make that difference between dynamic or not, it would be working for me ;-).

BuerkiJ avatar Aug 10 '22 06:08 BuerkiJ

Would you mind creating a PR with some tests such that we can discuss this on actual code?

filmor avatar Oct 28 '22 12:10 filmor