Portable classes from existing module without modifying module?

Hello all,

I’ve been trying to figure out how to turn classes from a package I wrote into Anvil portable classes, without needing to add Anvil-specific code to my original modules and, ideally, without having to modify my original code at all. Although I’ve found a way that avoids Anvil-specific code, it still does require slight modifications to my classes, and worse, it feels hackish. I wouldn’t be surprised if there’s an easier and/or more elegant solution.

Here’s my issue. I have classes set up as follows:

#./mypackage/mymodule.py

class Foo:
    #interesting Foo stuff

class Bar:
    def __init__(self):
          #interesting Bar stuff
          self.foo=Foo()

If it’s just Foo I want as a portable class, no problem — I can do the following from an intermediary anvil client module.

#./intermediary.py
from .mypackage.mymodule import Foo as originalFoo

@anvil.server.portable_class
class Foo(originalFoo):
    pass

And then, I would just:

from .intermediary import Foo

wherever I would have previously done:

from .mypackage.mymodule import Foo

That works fine, without my needing to modify my original module’s code.

However, once I want Bar to be a portable class too, I run into a problem — the Foo referred to by Bar’s __init__ is not the new portable Foo. Which means I can’t make Bar a portable class using the same strategy I’d used for Foo.

So I proceeded as follows, but felt icky doing so. I modified Bar’s original code in mymodule.py to read:

class Bar:

    foo_class=Foo

    def __init__(self):
          self.foo=foo_class()

And, then in my client-side module, I did the following:

#./intermediary.py

from .mypackage.mymodule import (
     Foo as originalFoo,
     Bar as originalBar,
     )

@anvil.server.portable_class
class Foo(originalFoo):
    pass

@anvil.server.portable_class
class Bar(originalBar):
    foo_class = Foo

This does work, but it required modification to my original code in a way that makes it less readable. Is there a way to avoid this? Is there something simpler or more elegant that I’m missing?

Thank you,
Dan

the pattern you’ve described is a common pattern i’ve seen in the cpython tests suite

class CommonTest(unittest.TestCase):
    # The type to be tested
    type2test = None
    ### common sequences tests

class ListTest(CommonTest):
    type2test = list
    ### list tests

You could also use super instead

@anvil.server.portable_class
class Bar(originalBar):
    def __init__(self):
        super().__init__()
        self.foo = Foo()

Well, that makes me feel better. Thank you! I figured I was doing something super un-Pythonic (it’s usually a safe bet that that’s the issue!).

I’ll keep your example using super in mind for future simple use cases, although in my description of the code, I glossed over complexity that I think could make that solution more brittle for my current circumstance. (What I had distilled down to self.foo=Foo() is really about 20 lines of code, involving lists and dicts containing instances of Foo – ie, lots of internal implementation that I don’t want my intermediary module to know/care about.)

1 Like