Today I ran into some strange behaviour with the server modules of my app.
My app has two defined server modules, each containing specific functions. However, some functions from the module1 have to be called from within module2 and viceversa. To avoid delay caused by anvil.server.call(), I always imported the required functions using from module1 import function1, function2 and then called the functions directly from module2’s code.
Today, I’ve tried to make a simple change, adding import of functions from module1 to module2 resulting in the following code:
module1
from .module2 import hello2
@anvil.server.callable
def hello1():
print("Hello1!")
hello2()
def completely_unrelated_function():
print("Importing me will cause an ImportError!")
module2
from .module1 import completely_unrelated_function
@anvil.server.callable
def hello2():
print("Hello2!")
As soon as I do this, the whole app crashes throwing an ImportError: cannot import name 'completely_unrelated_function'. In another app using the same scenario, I got an ImportError because of (unknown location). Is this expected behaviour?
What I tried
I can think of two possible solutions for this problem.
Use anvil.server.call() to call functions defined in other modules, possibly causing some delay because of the server calls
Import the functions I need only inside of the functions that use them, not globally for the whole module, having to copy the import statement in every function that requires it.
Is there anything else that I can do? Has anyone else run into this problem?
Sorry if my question sounds stupid, I have only basic experience with python and I never encountered anything like this.
Thank you,
I absolutely forgot this was also a feasible option. And it’s definitely a better idea than to have import statements in almost every function
yeah circular imports are a problem in the server environment
any time you do anvil.server.call all server modules will be loaded,
since anvil doesn’t know ahead of time which functions are registered where.
(slightly different with persistent server modules)
Doing a circular import like that in any python environment would break Here’s an example test case from cpython
def test_circular_from_import(self):
with self.assertRaises(ImportError) as cm:
import test.test_import.data.circular_imports.from_cycle1
with the two failing files that look like
# from_cycle1
from .from_cycle2 import a
b = 1
# from_cycle2
from .from_cycle1 import b
a = 1
Shame the error message isn’t nicer for the server module circular import
Not only all the modules will be loaded, they will (most likely) be loaded in alphabetical order, and this may cause unexpected failures when you assume for example MainModule will be loaded first, while AnotherModule will be loaded first because it comes first in alphabetical order.
Thank you very much for the additional info.
I’ve run into the problem with circular imports before, but I was trying to import whole packages back then. I just didn’t realise that circular imports also happen when you import only specific functions from the modules.
In fact, importing specific symbols is much more likely to trip over circular imports. I found that out the hard way, after a year of Python programming.
Importing specific symbols looks like great and easy documentation, until you trip over that little consequence.