Kitware / trame

Trame lets you weave various components and technologies into a Web Application solely written in Python.
https://kitware.github.io/trame/
Other
413 stars 54 forks source link

TypeError when calling isinstance on a TrameApp decorated object #594

Open MattTheCuber opened 1 week ago

MattTheCuber commented 1 week ago

Describe the bug

Using isinstance on a @TrameApp() decorated class throws a TypeError.

TypeError: isinstance() arg 2 must be a type, a tuple of types, or a union

To Reproduce

Steps to reproduce the behavior:

  1. Run the following code.

Code

from trame.decorators import TrameApp

@TrameApp()
class TestApp:
    server = None

t = TestApp()
print(isinstance(t, TestApp))

Expected behavior

I would assume isinstance would work. This is not a critical problem though as we have other workarounds.

The problem is that the @TrameApp() decorator returns a function that instantiates the class instead of returning a class itself. The TypeError is thrown because we are checking if a object is an instance of a function, which is not possible to check.

Maybe isinstance would never be a good way to handle this, either way it would be very useful to have a good way of checking the type of our app's instances.

Platform:

OS:

jourdain commented 1 week ago

That is a good issue but I'm not sure how to solve that... maybe by doing the following

from trame.decorators import TrameApp

class TestApp:
    server = None

t = TrameApp()(TestApp)()
print(isinstance(t, TestApp))
MattTheCuber commented 1 week ago

That is a solution, although fairly hard to follow in practice

jourdain commented 1 week ago

yes, I agree and I'm not sure how to solve that.

MattTheCuber commented 1 week ago

I asked Chat and it suggested creating a wrapper class with the decorator.

class TrameApp:
    def __call__(self, klass):
        class WrappedClass(klass):
            ...

        return WrappedClass

It could work, but then it could introduce other problems with type checking since type(t) would return WrappedClass instead of TestApp.

psavery commented 1 week ago

We might be able to re-design the class a little bit to support this. I might look into it later this week.

MattTheCuber commented 1 week ago

I just found another issue that the function wrapper may be causing. Not quite sure why this is occuring though:

from trame.decorators import TrameApp

@TrameApp()
class TestApp:
    server = None

@TrameApp()
class TestApp2(TestApp):
    server = None

t = TestApp()
t2 = TestApp2()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[2], line 10
      4 @TrameApp()
      5 class TestApp:
      6     server = None
      9 @TrameApp()
---> 10 class TestApp2(TestApp):
     11     server = None
     14 t = TestApp()

TypeError: function() argument 'code' must be code, not str

If this a separate problem, I can make a new issue. I will look into it more tomorrow probably.

jourdain commented 1 week ago

I think this is related because the decorated TestApp is actually a function and not a class which prevent you from inheriting it.