What I’m trying to do:
I want to use a class defined in my uplink code in a server module in anvil.
I can use functions defined in the same uplink, so the general connection works.
I define functions in uplink with @anvil.server.callable
and can use them in server code with anvil.server.call('function_name')
.
Classes I define in the uplink with @anvil.server.portable_class(_'class_name')
, but I don’t know how to use them in the server code then.
I read this entry (How do I use portable classes with uplink? (updated)), but it doesn’t help me because it deals with the reverse case, where a class is defined in the server code and then should be used in the uplink code.
I would be very happy if someone can help me.
I’m not sure I quite follow what makes the question different.
So long as you have the same class defined in both uplink code and client code with the same name then you should be able to send instances of that class between uplink, anvil server and client.
In Anvil - a portable class is usually defined in client code, that way you can send instance from client to server and back again.
Since you’ll be using the named version of portable_class
i.e. anvil.server.portable_class("my_class_name")
, you’ll need to directly import this class in your server code.
To get started you can try the following and extend it
# Client - Module1
import anvil.server
@anvil.server.portable_class("Foo")
class Foo:
pass
# ServerModule1
import anvil.server
from .Module1 import Foo
# we need to import Foo on the server because we have assigned a name
@anvil.server.callable
def say_hello_server(x):
print(f"hello, {x}")
y = anvil.server.call("say_hello_uplink", x)
print(f"hello, {y}")
return y
# uplink script
import anvil.server
anvil.server.connect("<your Server Uplink key>")
# Same definition as the anvil version
@anvil.server.portable_class("Foo")
class Foo:
pass
@anvil.server.callable
def say_hello_uplink(x):
print(f"hello, {x}")
return x
Now you can add a button on the client and do
def button_click(self, **event_args):
from ..Module1 import Foo
rv = anvil.server.call("say_hello_server", Foo())
print(rv)
If the above doesn’t help, maybe we need to check what the use case is just to make sure that we’re solving the right question.
What do you want the portable class to be able to do?
Thank you for your answer. So according to your example, a class must always be defined in a client module if you want to use it in a server module?
My concern is simply this: I have an extensive class defined in Python code with various functions etc. I would now like to use this class (and its functions) in an Anvil server module. I thought that this is possible if I simply uplink the Python script with the class to Anvil. With single functions this works. But with the whole class it doesn’t seem to work that way?!
Unfortunately, I still don’t quite understand why I need to define the class both in an Anvil client module and in the uplink. After all, if I define it in the client module, then I can already use it in the server module, so why should I define it again in the uplink? I would like to use the “direct way” from uplink code to server module without copying the class into an additional client module.
I think we might need to see more code to know what the class is trying to do and how you’re trying to use it before we get too far into the weeds here.
I can just define my classes in a client module, no problem. I thought I can avoid the “move” by using an uplink, but that’s how it works.
Let us know if you get stuck.
Just to expand on the explanation from earlier.
Portable classes are most useful when you need to transfer objects that aren’t typically serializable (anything that’s not JSON) across the network. Instead of sending the object, only its serializable data is transmitted. Anvil then recreates the object on the receiving end.
For successful object reconstruction, Anvil requires the serializable data and the class definition. When not using uplink, Anvil has access to both client and server modules, allowing it to find the necessary class definition and create the instance from the serialized data.
However, with uplink, defining your class in just one location will cause problems. In this case, Anvil receives the serialized data but lacks the class definition needed to recreate the instance on the receiving end.
This should also help to understand why we usually define portable classes in client modules - since server modules can access client modules but client modules can’t access server modules.
On a separate note, there are various approaches to avoid repeating yourself in server and uplink code, but that’s probably a different topic.
There are three computers running python code:
- the Anvil server, running the python version you select in the settings
- the client, running Skulpt, a python interpreter written in Javascript
- the machine(s) where the uplink code is running on whatever python interpreter they run
The Anvil server and the client see the files defined in the app. The server can see both client and server code folders, while the client can only see the client code folder, which is packaged by the server and sent to the client so it can be executed by Skulpt.
The uplink doesn’t see any of those folders. If you want to use the definition of a class, portable or not, you need to have it defined in the computer where the uplink is running.
Using the definition is one thing, executing is another.
If you want to use the definition, like declaring a class, portable or not, you need the file that defines that class in your computer, so you can execute that code in your computer.
If you want to execute on the other computer the code defined in that other computer, then you can ask “please run a function with this name and with these arguments and let me know the result”. You don’t need the definition of the server callables. That’s what server callables are for, they work in both Anvil-uplink and uplink-Anvil directions (and client-[Anvil|uplink]).
1 Like